59

I need to document with Swagger an API that uses, both as input and output, maps of objects, indexed by string keys.

Example:

{
    "a_property": {
        "foo": {
            "property_1": "a string 1",
            "property_2": "a string 2"
        },
        "bar": {
            "property_1": "a string 3",
            "property_2": "a string 4"
        }
    }
}

"foo" and "bar" can be any string keys, but they should be unique among the set of keys.

I know that, with Swagger, I can define an array of objects, but this gives a different API since we then would have something as:

{
    "a_property": [
        {
            "key": "foo"
            "property_1": "a string 1",
            "property_2": "a string 2"
        },
        {
            "key": "bar"
            "property_1": "a string 3",
            "property_2": "a string 4"
        }
    ]
}

I have read the 'Open API Specification' - 'Add support for Map data types #38' page. As far as I understand, it recommends to use additionalProperties, but it doesn't seem to answer my need (or it doesn't work with Swagger UI 2.1.4 that I use). Did I miss something?

So far I have found the following work-around (in Swagger JSON):

a_property: {
    description: "This is a map that can contain several objects indexed by different keys.",
    type: object,
    properties: {
        key: {
            description: "map item",
            type: "object",
            properties: {
                property_1: {
                    description: "first property",
                    type: string
                },
                property_2: {
                    description: "second property",
                    type: string
                }
            }
        }
    }
}

This almost does the job, but the reader has to understand that "key" can be any string, and can be repeated several times.

Is there a better way to achieve what I need?

1

4 Answers 4

65

Using additionalPropertiesis the proper way to describe hashmap with OpenAPI (fka. Swagger) Specification but Swagger UI do not render them for now.

The issue is tracked here https://github.com/swagger-api/swagger-ui/issues/1248

Meanwhile you can use this trick: define a non required property (defaultin the example below) of the same type of the map's objects and give some hint within the description:

swagger: "2.0"
info:
  version: 1.0.0
  title: Hashmap
  
paths: {}

definitions:
  MapItem:
    properties:
      firstname:
        type: string
      lastname:
        type: string
  Map:
    description: a (key, MapItem) map. `default`is an example key
    properties:
      default:
        $ref: '#/definitions/MapItem'
    additionalProperties:
      $ref: '#/definitions/MapItem'

This description does not modify API contract and improves documentation.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for finding the reference of the related issue in SwaggerUI. Unfortunately I don't have yet enough reputation to vote up your answer ;-)
As of this date, at least the javascript version of swagger-codegen ignores additional properties, so it could be a showstopper for some
additional properties just won't do it. Additional properties fail to address the fact that my value can have a specific schema
Great answer, but still not clear how to define string as a key type (in case of $ref as a key). It is not clear from swagger.io/docs/specification/data-models/dictionaries as well.
With OAS 3.0, definitions is no longer valid. Use components (and schemas under it) instead. For an example, check out swagger.io/docs/specification/using-ref
13

By using additionalProperties:

definitions:
  String-StringStringMap: # <-- use this as your result
    type: object
    additionalProperties:
      $ref: "#/definitions/StringStringMap"

  StringStringMap:
      type: object
      additionalProperties:
        type: string

this results a 2 level map:

{
  "additionalProp1": {
    "additionalProp1": "string",
    "additionalProp2": "string",
    "additionalProp3": "string"
  },
  "additionalProp2": {
    "additionalProp1": "string",
    "additionalProp2": "string",
    "additionalProp3": "string"
  },
  "additionalProp3": {
    "additionalProp1": "string",
    "additionalProp2": "string",
    "additionalProp3": "string"
  }
}

With same idea you can specify a 3 level map also.

Comments

5

If I understand it correctly, the basic problem is that there is no universally accepted JSON mapping for a Java Map, especially when the key is more complex than a string. I have seen that GSON takes one approach (treat the key as an object), whereas Jackson takes another (serialise the key to a string). The c# equivalent to a Map (a Dictionary) uses a third approach (treating each entry as a key-value object in its own right with properties called "Key" and "Value"). As Swagger tries to be agnostic to language and serialiser, this puts it in an impossible position.

Comments

1

you can simply use type as object. When we are parsing data from frontend we have no such thing Map<Key,value>. we are just sending objects. Map is up to backend stuf. that is why I am asking to use object as the type. In objects we can send key value pairs. as the example given below

  metaData:
    type: object
    example: {
      "heading":"comfirmation email"
    }

6 Comments

Can you clarify what you mean? As is, it's not entirely clear. That's especially important here, since there's an answer that's been widely validated by the community with 50 upvotes.
@JeremyCaney A map means that is a set of key value pairs. We are not passing Map<String, string> kind a thing from frontend. Instead of that we are parsing a object from frontend. that is why I asked to use object as the data type in that. this is an example I have used metaData: type: object example: { "heading":"comfirmation email" }
That’s helpful! Can I suggest editing your answer to include that detail?
Please add your comment suggestion to improve the answer .
@JeremyCaney yes you can edit
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.