8

How can I define a map with arbitrary keys in a Swagger model

Say I have the following internationalised model (in Ruby-style pseudocode, assuming use of something like Globalize)

class Thingy
  translates :name
  attribute :code
end

and my API wishes to be able to return something like

{
  "thingy": {
    "code": "barn", 
    "translations": {
      "default": "barn", 
      "en": "barn", 
      "ru": "cарай", 
      "fr": "grange", 
      "nl": "schuur"
    }
  }
}

but I don't want to restrict the range of translation keys in the actual API

I can define in my swagger doc

definitions:
  thingy:
    required:
      - code
    properties:
      code:
        type: string
    additionalProperties:
      translations:
        required:
          - default
        property:
          default:
            type: string
        additonalProperties: string

That validates but the Swagger Codegen won't generate anything off the additionalProperties and it's not very explicit compared to somehow being able to define a map type with a mix of required and arbitrary keys.

Anyone working with internationalisation is going to face similar issues so my question is, how have other people dealt with this scenario?

Dave Sag
  • 12,289
  • 8
  • 77
  • 118

2 Answers2

9

This works under swagger-codegen-2.1.1-M1 (Java/JavaJaxRS) ... with Ron's suggestions ...

The YAML ...

translation:
  required:
    - default
  properties:
    default:
      type: string
  additionalProperties:
    type: string

thingy:
  required:
    - code
  properties:
    code:
      type: string
    translations:
      $ref: '#/definitions/translation'

creates a Map with a 'default' attribute ...

public class Translation extends HashMap<String, String> {

    /**
     * 
     */
    @Expose
    private String _default = null;

    /**
     * @return  _default the _default
     */
    public String getDefault() {
        return _default;
    }

    /**
     * @param  _default to set
     */
    public void setDefault(String _default) {
        this._default = _default;
    }

}

Which in turn is embedded in a Thingy .....

public class Thingy  {

    /**
     * 
     */
    @Expose
    private String code = null;

    /**
     * 
     */
    @Expose
    private Translation translations = null;

    /**
     * @return  code the code
     */
    public String getCode() {
        return code;
    }

    /**
     * @param  code to set
     */
    public void setCode(String code) {
        this.code = code;
    }

    /**
     * @return  translations the Translations
     */
    public Translation getTranslations() {
        return translations;
    }

    /**
     * @param  translations the Translations to set
     */
    public void setTranslations(Translation translations) {
        this.translations = translations;
    }

}
jreece
  • 120
  • 4
2

While the definition above is theoretically valid, it does not translate to what you're trying to describe nor is it really supported by Swagger.

In order to describe the structure you want, you'd need the following definition:

thingy:
  type: object
  required:
    - code
  properties:
    code:
      type: string
    translations:
      type: object
      required:
          - default
      properties:
          default:
            type: string
      additonalProperties: 
          type: string

While you can define the internal object inline as above, I'd highly recommend you externalize the definition and use $ref to reference it from the translations definition.

As for the code generator, support for maps has been introduced lately so it should work. If you find that it doesn't, please open an issue directly on the project containing a sample Swagger definition to help with debugging.

Ron
  • 12,508
  • 3
  • 45
  • 39
  • Thanks Ron, that's helpful. I've expanded on this a bit taking your suggestions into account - see gist at https://gist.github.com/davesag/c8cf393f1a14d4df757e – Dave Sag Apr 16 '15 at 07:03
  • Added a comment there. – Ron Apr 16 '15 at 07:08