13

This is my first foray into Swagger so please be gentle.

I have the following definitions:

definitions:
  Payload:
    type: object
    properties:
      indicators:
        type: array
        items:
          $ref: '#/definitions/Indicator'
  Indicator:
    type: object
    properties:
      type:
        type: string
      computeOn:
        type: array
        items:
          type: string
        default:
          - close
      parameters:
        type: object
  BBANDS:
    properties:
      type:
        type: string
        default: BBANDS
      computeOn:
        type: array
        items:
          type: string
        default:
          - close
      parameters:
        type: object
        properties:
          timeperiod:
            type: integer
            format: int32
            default: 5
          nbdevup:
            type: integer
            format: int32
            default: 2
          nbdevdn:
            type: integer
            format: int32
            default: 2
          matype:
            type: integer
            format: int32
            default: 0
  DEMA:
    properties:
      type:
        type: string
        default: DEMA
      computeOn:
        type: array
        items:
          type: string
        default:
          - close
      parameters:
        type: object
        properties:
          timeperiod:
            type: integer
            format: int32
            default: 5

So Payload has a property called indicator which is an array of Indicators. The BBANDS and DEMA are models which are of type Indicator (which I know doesn't translate to Swagger). What I'd like to do is define an array of the actual models with their defaults, in this case BBANDS and DEMA. Something like this:

definitions:
  Payload:
    type: object
    properties:
      indicators:
        type: array
        items:
          - '#/definitions/BBANDS'
          - '#/definitions/DEMA'

or

definitions:
  Payload:
    type: object
    properties:
      indicators:
        type: array
        items:
          - $ref '#/definitions/BBANDS'
          - $ref '#/definitions/DEMA'

Neither of which work of course. The reason is while the Indicator model describes an indicator correctly, different indicators can have a different parameter set.

Is there a way to essentially define a list of several models or perhaps map the BBANDS and DEMA models into Indicator?

Edit: Result of using @Helen's first suggestion in the Swagger Editor

enter image description here

Jean-Rémy Revy
  • 5,607
  • 3
  • 39
  • 65
Jason Strimpel
  • 14,670
  • 21
  • 76
  • 106

2 Answers2

11

Swagger/OpenAPI 2.0 does not support multiple types for items, but there are a couple of ways to describe what you need.

Option 1 - Model Inheritance

As long as you have one field that is common between the models and can be used to distinguish between them, you can use model inheritance:

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaDiscriminator https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#composition-and-inheritance-polymorphism

In your example, this property is type (type="BBANDS" or type="DEMA"). So you can:

  • Inherit the BBANDS and DEMA models from Indicator by using allOf.
  • Add discriminator: type to Indicator to indicate that the type property will be used to distinguish between the sub-models.
  • Define Payload as an array of Indicator. This way it can actually be an array of BBANDS or an array of DEMA.

definitions:
  Payload:
    type: object
    properties:
      indicators:
        type: array
        items:
          $ref: '#/definitions/Indicator'

  Indicator:
    type: object
    properties:
      type:
        type: string
        # Limit the possible values if needed
        #enum:
        #  - BBANDS
        #  - DEMA
      computeOn:
        type: array
        items:
          type: string
        default:
          - close

    # The "type" property will be used to distinguish between the sub-models.
    # The value of the "type" property MUST be the schema name, that is, "BBANDS" or "DEMA".
    # (Or in other words, the sub-model schema names must match possible values of "type".)
    discriminator: type
    required:
      - type

  BBANDS:
    allOf:
      - $ref: '#/definitions/Indicator'
      - type: object
        properties:
          parameters:
            type: object
            properties:
              timeperiod:
                type: integer
                format: int32
                default: 5
              nbdevup:
                type: integer
                format: int32
                default: 2
              nbdevdn:
                type: integer
                format: int32
                default: 2
              matype:
                type: integer
                format: int32
                default: 0
  DEMA:
    allOf:
      - $ref: '#/definitions/Indicator'
      - type: object
        properties:
          parameters:
            type: object
            properties:
              timeperiod:
                type: integer
                format: int32
                default: 5

Option 2 - Single Model

If all parameters are integer, you can have a single model Indicator with parameters defined as a hashmap. But in this case you lose the ability to define the exact parameters for specific indicator types.

definitions:
  Indicator:
    type: object
    properties:
      type:
        type: string
        enum:
          - BBANDS
          - DEMA
      computeOn:
        type: array
        items:
          type: string
        default:
          - close
      parameters:
        type: object
        properties:
          # This is a common parameter in both BBANDS and DEMA
          timeperiod:
            type: integer
            format: int32
            default: 5
        # This will match additional parameters "nbdevup", "nbdevdn", "matype" in BBANDS
        additionalProperties:
          type: integer
Jean-Rémy Revy
  • 5,607
  • 3
  • 39
  • 65
Helen
  • 87,344
  • 17
  • 243
  • 314
  • I think the first option will work. I will check it out tomorrow and come back. Thanks for the thorough explanation. – Jason Strimpel Dec 12 '16 at 12:01
  • Ok so I pasted this into the swagger editor and in the indicator section there is text in red "Object is missing the required property 'type'", the `type` is a text field and parameters are empty (see screen print). This is the same as if just defining the `Indicator` object. – Jason Strimpel Dec 13 '16 at 00:32
  • @JasonStrimpel: So you are using that definition as POST/PUT/PATCH request payload. That form is used to send a sample request to your API host, so you need to fill out the object fields that will be included in the sample request. The error means you need to specify a value for `type` since `type` is marked as a required property in your definition. – Helen Dec 13 '16 at 07:44
  • So when I do this with the JavaScript generated client, the json comes back with the different values of the sub-models, but when I set the returned data to my object, the only values that the object keeps is the type discriminator. All additional values are lost. Do you know if there is a way to fix this? Otherwise, I may just need to go with the single model. – srchulo Nov 11 '18 at 22:39
  • @srchulo Sorry I'm not familiar with the JavaScript codegen. I'd suggest that you [ask a new question](/questions/ask), and/or ask the developers of the codegen that you used (e.g. if you used Swagger Codegen, you can ask in the [Swagger Codegen repository](https://github.com/swagger-api/swagger-codegen/issues)). – Helen Nov 12 '18 at 08:11
  • @Helen, thanks for the response! That makes sense :) – srchulo Nov 13 '18 at 00:23
  • Hi good afternoon, I tried option 1 and it worked fine but it doesn't get reflected in the Swagger Editor, the "example" has the array with the same value all the time, it is not adding an example for each subtype, thanks – Diego Ramos Mar 09 '22 at 01:31
-1

AFAIK in array type can holds one type, if you want to have multiple types under an array, then need to define another super type and wrap the subtypes in it ( May be using object type ) like bellow. This restriction is because swagger-2.0 doesn't support all the features of json-schema.org, oneOf, anyOf, allOf etc are some of it.

But you can make use the third party extension option available with swagger-2.0. Where you can name a Key with x-, so that means you can include these oneOf like x-oneOf and when parsing the schema you do that using json-schema parser along with swagger schema parser.

One such example is given bellow,

Json-schema

{
  "id": "http://some.site.somewhere/entry-schema#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "description": "schema for an fstab entry",
  "type": "object",
  "required": [
    "storage"
  ],
  "properties": {
    "storage": {
      "type": "object",
      "oneOf": [
        {
          "$ref": "#/definitions/diskDevice"
        }
      ]
    },
    "deviceList": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/definitions/Device"
      }
    }
  },
  "definitions": {
    "diskDevice": {
      "type": "object",
      "properties": {
        "label": {
          "type": "string"
        }
      },
      "required": [
        "label"
      ]
    },
    "blockDevice": {
      "type": "object",
      "properties": {
        "blockId": {
          "type": "number"
        }
      },
      "required": [
        "blockId"
      ]
    },
    "CharDevice": {
      "type": "object",
      "properties": {
        "charDeviceName": {
          "type": "string"
        }
      },
      "required": [
        "charDeviceName"
      ]
    },
    "Device": {
      "type": "object",
      "oneOf": [
        {
          "$ref": "#/definitions/diskDevice"
        },
        {
          "$ref": "#/definitions/blockDevice"
        },
        {
          "$ref": "#/definitions/CharDevice"
        }
      ]
    }
  }
}

Data or Payload

{
"storage": {"label": "adsf"},
"deviceList": [{"label": "asdf"}, {"blockId": 23}, {"charDeviceName": "asdf"}]
}

Use this site for play around with your sample data - http://www.jsonschemavalidator.net/

Note the deviceList property and how it got constructed. Hope this helps you.

Jean-Rémy Revy
  • 5,607
  • 3
  • 39
  • 65
Haridas N
  • 529
  • 5
  • 20