From 30d3fa3abadb408ec9aa4113aa1c3bd1643a0ae0 Mon Sep 17 00:00:00 2001 From: Tony Tam Date: Mon, 8 May 2017 15:49:47 -0700 Subject: [PATCH 1/4] documented the discriminator property --- versions/3.0.md | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/versions/3.0.md b/versions/3.0.md index cd6a6e2826..d19a56669f 100644 --- a/versions/3.0.md +++ b/versions/3.0.md @@ -3012,6 +3012,125 @@ schemas: - packSize ``` +#### Discriminator Object + +When request bodies or response payloads may be one of a number of different schemas, a `discriminator` property can be used to aid in serialization, deserialization, and validation. The discriminator is a specific property in an object which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. + +##### Fixed Fields +Field Name | Type | Description +---|:---:|--- +propertyName | `string` | **required** the name of the property in the payload that will hold the discriminator value. + mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or references. + +The discriminator attribute is legal only when using one of the composite keywords `oneOf`, `anyOf`, `allOf`. + +In OAS 3.0, a response payload may be described to be exactly one of any number of types: + +``` +MyResponseType: + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Lizard' +``` + +which means the paylod _must_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. In this case, a discriminator will act as a "hint" to bypass validation and selection of the matching schema which may be a costly operation, depending on the complexity of the schema. We can then describe exactly which field tells us which schema to use: + + +``` +MyResponseType: + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Lizard' + discriminator: + property: pet_type +``` + +The expectation now is that a property with name `pet_type` _must_ be present in the response payload, and the value will correspond to the name of a schema defined in the OAS document. Thus the response payload: + +``` +{ + "id": 12345, + "pet_type": "Cat" +} +``` + +Will indicate that the `Cat` schema be used in conjunction with this payload. + +In scenarios where the value of the discriminator field does not match the schema name, an optional `mapping` definition may be used: + +``` +MyResponseType: + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Lizard' + discriminator: + property: pet_type + mapping: + cat: '#/components/schemas/Cat' +``` + +When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity where multiple schemas may satisfy a single payload. + +In both the `oneOf` and `anyOf` use cases, all possible schemas must be listed explicitly. To avoid redundancy, the discriminator may be added to a parent schema definition, and all schemas composing the parent schema in an `allOf` construct may be used as an alternate schema. + +For example: + +``` +components: + schemas: + Pet: + type: object + required: + - pet_type + properties: + pet_type: + type: string + discriminator: + propertyName: pet_type + mapping: + cachorro: Dog + Cat: + allOf: + - $ref: '#/components/schemas/Pet' + - type: object + # all other properties specific to a `Cat` + properties: + name: + type: string + Dog: + allOf: + - $ref: '#/components/schemas/Pet' + - type: object + # all other properties specific to a `Dog` + properties: + bark: + type: string +``` + +a payload like this: + +``` +{ + "pet_type": "Cat", + "name": "misty" +} +``` + +will indicate that the `Cat` schema be used. Likewise this schema: + +``` +{ + "pet_type": "cachorro", + "bark": "soft" +} +``` + +will map to `Dog` because of the definition in the `mappings` element. + + #### XML Object A metadata object that allows for more fine-tuned XML model definitions. From 10ce3e0e78bda847005aaadc57ec3337ea06b02f Mon Sep 17 00:00:00 2001 From: Tony Tam Date: Thu, 11 May 2017 15:25:48 -0700 Subject: [PATCH 2/4] updated per feedback --- versions/3.0.md | 335 +++++++++++++++++++++++++----------------------- 1 file changed, 177 insertions(+), 158 deletions(-) diff --git a/versions/3.0.md b/versions/3.0.md index d19a56669f..390a3d3e7e 100644 --- a/versions/3.0.md +++ b/versions/3.0.md @@ -2663,7 +2663,7 @@ Other than the JSON Schema subset fields, the following fields MAY be used for f Field Name | Type | Description ---|:---:|--- nullable | `boolean` | Allows sending a `null` value for the defined schema. Default value is `false`. -discriminator | `string` | Adds support for polymorphism. The discriminator is the schema property name that is used to differentiate between other schema that inherit this schema. The property name used MUST be defined at this schema and it MUST be in the `required` property list. When used, the value MUST be the name of this schema or any schema that inherits it. See [Composition and Inheritance](#schemaComposition) for more details. +discriminator | [Discriminator Object](#discriminatorObject) | Adds support for polymorphism. The discriminator is the schema property name that is used to differentiate between other schema that inherit this schema. The property name used MUST be defined at this schema and it MUST be in the `required` property list. When used, the value MUST be the name of this schema or any schema that inherits it. See [Composition and Inheritance](#schemaComposition) for more details. readOnly | `boolean` | Relevant only for Schema `"properties"` definitions. Declares the property as "read only". This means that it MAY be sent as part of a response but SHOULD NOT be sent as part of the request. If property is marked as `readOnly` being `true` and is in the `required` list, the `required` will take effect on the response only. A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`. Default value is `false`. writeOnly | `boolean` | Relevant only for Schema `"properties"` definitions. Declares the property as "write only". This means that it MAY be sent as part of a request but SHOULD NOT be sent as part of the response. If property is marked as `writeOnly` being `true` and is in the `required` list, the `required` will take effect on the request only. A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`. Default value is `false`. xml | [XML Object](#xmlObject) | This MAY be used only on properties schemas. It has no effect on root schemas. Adds Additional metadata to describe the XML representation format of this property. @@ -2826,190 +2826,197 @@ example: ```json { - "schemas": { - "ErrorModel": { - "type": "object", - "required": [ - "message", - "code" - ], - "properties": { - "message": { - "type": "string" - }, - "code": { - "type": "integer", - "minimum": 100, - "maximum": 600 + "components": { + "schemas": { + "ErrorModel": { + "type": "object", + "required": [ + "message", + "code" + ], + "properties": { + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "minimum": 100, + "maximum": 600 + } } - } - }, - "ExtendedErrorModel": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorModel" - }, - { - "type": "object", - "required": [ - "rootCause" - ], - "properties": { - "rootCause": { - "type": "string" + }, + "ExtendedErrorModel": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorModel" + }, + { + "type": "object", + "required": [ + "rootCause" + ], + "properties": { + "rootCause": { + "type": "string" + } } } - } - ] + ] + } } } } ``` ```yaml -schemas: - ErrorModel: - type: object - required: - - message - - code - properties: - message: - type: string - code: - type: integer - minimum: 100 - maximum: 600 - ExtendedErrorModel: - allOf: - - $ref: '#/components/schemas/ErrorModel' - - type: object +components: + schemas: + ErrorModel: + type: object required: - - rootCause + - message + - code properties: - rootCause: + message: type: string + code: + type: integer + minimum: 100 + maximum: 600 + ExtendedErrorModel: + allOf: + - $ref: '#/components/schemas/ErrorModel' + - type: object + required: + - rootCause + properties: + rootCause: + type: string ``` ###### Models with Polymorphism Support ```json { - "schemas": { - "Pet": { - "type": "object", - "discriminator": "petType", - "properties": { - "name": { - "type": "string" + "components": { + "schemas": { + "Pet": { + "type": "object", + "discriminator": "petType", + "properties": { + "name": { + "type": "string" + }, + "petType": { + "type": "string" + } }, - "petType": { - "type": "string" - } + "required": [ + "name", + "petType" + ] }, - "required": [ - "name", - "petType" - ] - }, - "Cat": { - "description": "A representation of a cat. Note that `Cat` will be used as the discriminator value.", - "allOf": [ - { - "$ref": "#/components/schemas/Pet" - }, - { - "type": "object", - "properties": { - "huntingSkill": { - "type": "string", - "description": "The measured skill for hunting", - "default": "lazy", - "enum": [ - "clueless", - "lazy", - "adventurous", - "aggressive" - ] - } + "Cat": { + "description": "A representation of a cat. Note that `Cat` will be used as the discriminator value.", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" }, - "required": [ - "huntingSkill" - ] - } - ] - }, - "Dog": { - "description": "A representation of a dog. Note that `Dog` will be used as the discriminator value.", - "allOf": [ - { - "$ref": "#/components/schemas/Pet" - }, - { - "type": "object", - "properties": { - "packSize": { - "type": "integer", - "format": "int32", - "description": "the size of the pack the dog is from", - "default": 0, - "minimum": 0 - } + { + "type": "object", + "properties": { + "huntingSkill": { + "type": "string", + "description": "The measured skill for hunting", + "default": "lazy", + "enum": [ + "clueless", + "lazy", + "adventurous", + "aggressive" + ] + } + }, + "required": [ + "huntingSkill" + ] + } + ] + }, + "Dog": { + "description": "A representation of a dog. Note that `Dog` will be used as the discriminator value.", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" }, - "required": [ - "packSize" - ] - } - ] + { + "type": "object", + "properties": { + "packSize": { + "type": "integer", + "format": "int32", + "description": "the size of the pack the dog is from", + "default": 0, + "minimum": 0 + } + }, + "required": [ + "packSize" + ] + } + ] + } } } } ``` ```yaml -schemas: - Pet: - type: object - discriminator: petType - properties: - name: - type: string - petType: - type: string - required: - - name - - petType - Cat: ## "Cat" will be used as the discriminator value - description: A representation of a cat - allOf: - - $ref: '#/components/schemas/Pet' - - type: object +components: + schemas: + Pet: + type: object + discriminator: + propertyName: petType properties: - huntingSkill: + name: + type: string + petType: type: string - description: The measured skill for hunting - default: lazy - enum: - - clueless - - lazy - - adventurous - - aggressive - required: - - huntingSkill - Dog: ## "Dog" will be used as the discriminator value - description: A representation of a dog - allOf: - - $ref: '#/components/schemas/Pet' - - type: object - properties: - packSize: - type: integer - format: int32 - description: the size of the pack the dog is from - default: 0 - minimum: 0 required: - - packSize + - name + - petType + Cat: ## "Cat" will be used as the discriminator value + description: A representation of a cat + allOf: + - $ref: '#/components/schemas/Pet' + - type: object + properties: + huntingSkill: + type: string + description: The measured skill for hunting + default: lazy + enum: + - clueless + - lazy + - adventurous + - aggressive + required: + - huntingSkill + Dog: ## "Dog" will be used as the discriminator value + description: A representation of a dog + allOf: + - $ref: '#/components/schemas/Pet' + - type: object + properties: + packSize: + type: integer + format: int32 + description: the size of the pack the dog is from + default: 0 + minimum: 0 + required: + - packSize ``` #### Discriminator Object @@ -3034,7 +3041,7 @@ MyResponseType: - $ref: '#/components/schemas/Lizard' ``` -which means the paylod _must_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. In this case, a discriminator will act as a "hint" to bypass validation and selection of the matching schema which may be a costly operation, depending on the complexity of the schema. We can then describe exactly which field tells us which schema to use: +which means the paylod _must_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. In this case, a discriminator may act as a "hint" to shortcut validation and selection of the matching schema which may be a costly operation, depending on the complexity of the schema. We can then describe exactly which field tells us which schema to use: ``` @@ -3044,7 +3051,7 @@ MyResponseType: - $ref: '#/components/schemas/Dog' - $ref: '#/components/schemas/Lizard' discriminator: - property: pet_type + propertyName: pet_type ``` The expectation now is that a property with name `pet_type` _must_ be present in the response payload, and the value will correspond to the name of a schema defined in the OAS document. Thus the response payload: @@ -3058,7 +3065,7 @@ The expectation now is that a property with name `pet_type` _must_ be present in Will indicate that the `Cat` schema be used in conjunction with this payload. -In scenarios where the value of the discriminator field does not match the schema name, an optional `mapping` definition may be used: +In scenarios where the value of the discriminator field does not match the schema name or implicit mapping is not possible, an optional `mapping` definition may be used: ``` MyResponseType: @@ -3066,12 +3073,16 @@ MyResponseType: - $ref: '#/components/schemas/Cat' - $ref: '#/components/schemas/Dog' - $ref: '#/components/schemas/Lizard' + - $ref: 'https://gigantic-server.com/schemas/Monster/schema.json' discriminator: property: pet_type mapping: - cat: '#/components/schemas/Cat' + dog: '#/components/schemas/Dog' + monster: 'https://gigantic-server.com/schemas/Monster/schema.json' ``` +Here the discriminator _value_ of `dog` will map to the schema `#/components/schemas/Dog`, rather than the default (implicit) value of `Dog`. If the discriminator _value_ does not match a implicit or explicit mapping, no schema can be determined and validation should fail. + When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity where multiple schemas may satisfy a single payload. In both the `oneOf` and `anyOf` use cases, all possible schemas must be listed explicitly. To avoid redundancy, the discriminator may be added to a parent schema definition, and all schemas composing the parent schema in an `allOf` construct may be used as an alternate schema. @@ -3108,6 +3119,14 @@ components: properties: bark: type: string + Lizard: + allOf: + - $ref: '#/components/schemas/Pet' + - type: object + # all other properties specific to a `Lizard` + properties: + lovesRocks: + type: boolean ``` a payload like this: From 5064b7cc6bec2b51e3971a1a76017c12f586cbdd Mon Sep 17 00:00:00 2001 From: Tony Tam Date: Fri, 12 May 2017 09:47:47 -0700 Subject: [PATCH 3/4] comments per TDC --- versions/3.0.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/versions/3.0.md b/versions/3.0.md index 390a3d3e7e..7b4cb62f26 100644 --- a/versions/3.0.md +++ b/versions/3.0.md @@ -2995,7 +2995,6 @@ components: huntingSkill: type: string description: The measured skill for hunting - default: lazy enum: - clueless - lazy @@ -3041,7 +3040,7 @@ MyResponseType: - $ref: '#/components/schemas/Lizard' ``` -which means the paylod _must_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. In this case, a discriminator may act as a "hint" to shortcut validation and selection of the matching schema which may be a costly operation, depending on the complexity of the schema. We can then describe exactly which field tells us which schema to use: +which means the paylod _MUST_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. In this case, a discriminator may act as a "hint" to shortcut validation and selection of the matching schema which may be a costly operation, depending on the complexity of the schema. We can then describe exactly which field tells us which schema to use: ``` @@ -3054,7 +3053,7 @@ MyResponseType: propertyName: pet_type ``` -The expectation now is that a property with name `pet_type` _must_ be present in the response payload, and the value will correspond to the name of a schema defined in the OAS document. Thus the response payload: +The expectation now is that a property with name `pet_type` _MUST_ be present in the response payload, and the value will correspond to the name of a schema defined in the OAS document. Thus the response payload: ``` { @@ -3075,13 +3074,13 @@ MyResponseType: - $ref: '#/components/schemas/Lizard' - $ref: 'https://gigantic-server.com/schemas/Monster/schema.json' discriminator: - property: pet_type + propertyName: pet_type mapping: dog: '#/components/schemas/Dog' monster: 'https://gigantic-server.com/schemas/Monster/schema.json' ``` -Here the discriminator _value_ of `dog` will map to the schema `#/components/schemas/Dog`, rather than the default (implicit) value of `Dog`. If the discriminator _value_ does not match a implicit or explicit mapping, no schema can be determined and validation should fail. +Here the discriminator _value_ of `dog` will map to the schema `#/components/schemas/Dog`, rather than the default (implicit) value of `Dog`. If the discriminator _value_ does not match a implicit or explicit mapping, no schema can be determined and validation should fail. Note, mapping keys MUST be string values, but tooling may response values to strings for comparison. When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity where multiple schemas may satisfy a single payload. From b07e49fe3242a75ab2f8022b9ad165e769583134 Mon Sep 17 00:00:00 2001 From: Tony Tam Date: Fri, 19 May 2017 09:25:57 -0700 Subject: [PATCH 4/4] clarifications on discriminator object, disallow inline schemas --- versions/3.0.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/versions/3.0.md b/versions/3.0.md index d33fa0f6d2..f180c2c92d 100644 --- a/versions/3.0.md +++ b/versions/3.0.md @@ -2663,7 +2663,7 @@ Other than the JSON Schema subset fields, the following fields MAY be used for f Field Name | Type | Description ---|:---:|--- nullable | `boolean` | Allows sending a `null` value for the defined schema. Default value is `false`. -discriminator | [Discriminator Object](#discriminatorObject) | Adds support for polymorphism. The discriminator is the schema property name that is used to differentiate between other schema that inherit this schema. The property name used MUST be defined at this schema and it MUST be in the `required` property list. When used, the value MUST be the name of this schema or any schema that inherits it. See [Composition and Inheritance](#schemaComposition) for more details. +discriminator | [Discriminator Object](#discriminatorObject) | Adds support for polymorphism. The discriminator is an object name that is used to differentiate between other schemas which may satisfy the payload description. See [Composition and Inheritance](#schemaComposition) for more details. readOnly | `boolean` | Relevant only for Schema `"properties"` definitions. Declares the property as "read only". This means that it MAY be sent as part of a response but SHOULD NOT be sent as part of the request. If property is marked as `readOnly` being `true` and is in the `required` list, the `required` will take effect on the response only. A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`. Default value is `false`. writeOnly | `boolean` | Relevant only for Schema `"properties"` definitions. Declares the property as "write only". This means that it MAY be sent as part of a request but SHOULD NOT be sent as part of the response. If property is marked as `writeOnly` being `true` and is in the `required` list, the `required` will take effect on the request only. A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`. Default value is `false`. xml | [XML Object](#xmlObject) | This MAY be used only on properties schemas. It has no effect on root schemas. Adds Additional metadata to describe the XML representation format of this property. @@ -3020,7 +3020,9 @@ components: #### Discriminator Object -When request bodies or response payloads may be one of a number of different schemas, a `discriminator` property can be used to aid in serialization, deserialization, and validation. The discriminator is a specific property in an object which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. +When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. + +Note, when using the discriminator, _inline_ schemas will not be considered when using the discriminator. ##### Fixed Fields Field Name | Type | Description