35
votes

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?

2
Personally it took me some time to understand why additionalProperties is the right answer: stackoverflow.com/a/41240118/110488 - Chen Levy

2 Answers

48
votes

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.

6
votes

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.