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

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

7 Comments

I think the first option will work. I will check it out tomorrow and come back. Thanks for the thorough explanation.
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.
@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.
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 Sorry I'm not familiar with the JavaScript codegen. I'd suggest that you ask a new question, 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).
|
-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.

Comments

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.