From f1d562908b111856a1cc30e0844b745cf8bc4a6a Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Thu, 6 Feb 2025 13:17:05 -0600 Subject: [PATCH 1/9] First draft of optional discriminator feature --- src/oas.md | 242 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 141 insertions(+), 101 deletions(-) diff --git a/src/oas.md b/src/oas.md index 4f025228b4..0d5d98083a 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2832,7 +2832,7 @@ JSON Schema implementations MAY choose to treat keywords defined by the OpenAPI | Field Name | Type | Description | | ---- | :----: | ---- | -| discriminator | [Discriminator Object](#discriminator-object) | Adds support for polymorphism. The discriminator is used to determine which of a set of schemas a payload is expected to satisfy. See [Composition and Inheritance](#composition-and-inheritance-polymorphism) for more details. | +| discriminator | [Discriminator Object](#discriminator-object) | The discriminator provides a "hint" for which of a set of schemas a payload is expected to satisfy. See [Composition and Inheritance](#composition-and-inheritance-polymorphism) for more details. | | xml | [XML Object](#xml-object) | This MAY be used only on property schemas. It has no effect on root schemas. Adds additional metadata to describe the XML representation of this property. | | externalDocs | [External Documentation Object](#external-documentation-object) | Additional external documentation for this schema. | | example | Any | A free-form field to include an example of an instance for this schema. To represent examples that cannot be naturally represented in JSON or YAML, a string value can be used to contain the example with escaping where necessary.

**Deprecated:** The `example` field has been deprecated in favor of the JSON Schema `examples` keyword. Use of `example` is discouraged, and later versions of this specification may remove it. | @@ -2870,9 +2870,15 @@ The OpenAPI Specification allows combining and extending model definitions using `allOf` takes an array of object definitions that are validated _independently_ but together compose a single object. While composition offers model extensibility, it does not imply a hierarchy between the models. -To support polymorphism, the OpenAPI Specification adds the [`discriminator`](#schema-discriminator) field. -When used, the `discriminator` indicates the name of the property that hints which schema definition is expected to validate the structure of the model. -As such, the `discriminator` field MUST be a required field. + +JSON Schema also provides the `anyOf` and `oneOf` keywords, which allow defining multiple schemas where at least one or exactly one of them must be valid, respectively. +As is the case with `allOf`, the schemas are validated _independently_. +These keywords can be used to describe polymorphism, where a single field can accept multiple types of values. + +The OpenAPI specification extends the JSON Schema support for polymorphism by adding the [`discriminator`](#schema-discriminator) field. +When used, the `discriminator` indicates the name of the property that hints which schema of an `anyOf` or `oneOf` is expected to validate the structure of the model. +The `discriminator` property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `default` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. + There are two ways to define the value of a discriminator for an inheriting instance. * Use the schema name. @@ -3135,71 +3141,58 @@ components: ###### Models with Polymorphism Support -```json -{ - "components": { - "schemas": { - "Pet": { - "type": "object", - "discriminator": { - "propertyName": "petType" - }, - "properties": { - "name": { - "type": "string" - }, - "petType": { - "type": "string" - } - }, - "required": ["name", "petType"] - }, - "Cat": { - "description": "A representation of a cat. Note that `Cat` will be used as the discriminating 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"] - } - }, - "required": ["huntingSkill"] - } - ] - }, - "Dog": { - "description": "A representation of a dog. Note that `Dog` will be used as the discriminating 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 - } - }, - "required": ["packSize"] - } - ] - } - } - } -} +The following example describes a `Pet` model that can represent either a cat or a dog, as distinguished by the `petType` property. Each type of pet has other properties beyond those of the base `Pet` model. An instance without a `petType` property, or with a `petType` property that does not match either `cat` or `dog`, is invalid. + +```yaml +components: + schemas: + Pet: + type: object + properties: + name: + type: string + required: + - name + - petType + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + Cat: + description: A pet cat + type: object + properties: + petType: + const: 'cat' + huntingSkill: + type: string + description: The measured skill for hunting + enum: + - clueless + - lazy + - adventurous + - aggressive + required: + - huntingSkill + Dog: + description: A pet dog + type: object + properties: + petType: + const: 'dog' + packSize: + type: integer + format: int32 + description: the size of the pack the dog is from + default: 0 + minimum: 0 + required: + - packSize ``` +###### Models with Polymorphism Support and a Discriminator field + +The following example extends the example of the previous section by adding a `discriminator` field to the `Pet` model. Note that the `discriminator` is only a hint to the consumer of the API, and does not change the validation outcome of the schema. + ```yaml components: schemas: @@ -3207,44 +3200,49 @@ components: type: object discriminator: propertyName: petType + mapping: + cat: '#/components/schemas/Cat' + dog: '#/components/schemas/Dog' properties: name: type: string + required: + - name + - petType + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + Cat: + description: A pet cat + type: object + properties: petType: + const: 'cat' + huntingSkill: type: string + description: The measured skill for hunting + enum: + - clueless + - lazy + - adventurous + - aggressive + required: + - huntingSkill + Dog: + description: A pet dog + type: object + properties: + petType: + const: 'dog' + packSize: + type: integer + format: int32 + description: the size of the pack the dog is from + default: 0 + minimum: 0 required: - - name - petType - Cat: # "Cat" will be used as the discriminating value - description: A representation of a cat - allOf: - - $ref: '#/components/schemas/Pet' - - type: object - properties: - huntingSkill: - type: string - description: The measured skill for hunting - enum: - - clueless - - lazy - - adventurous - - aggressive - required: - - huntingSkill - Dog: # "Dog" will be used as the discriminating 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 + - packSize ``` ###### Generic Data Structure Model @@ -3362,7 +3360,9 @@ components: #### Discriminator Object -When request bodies or response payloads may be one of a number of different schemas, a Discriminator Object gives a hint about the expected schema of the document. +When request bodies or response payloads may be one of a number of different schemas, these should use the JSON Schema `anyOf` or `oneOf` keywords to describe the possible schemas (see [Composition and Inheritance](#composition-and-inheritance-polymorphism)). + +A polymorphic schema MAY include a `discriminator` field, which defines the name of the property that may be used as a hint for which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. This hint can be used to aid in serialization, deserialization, and validation. The Discriminator Object does this by implicitly or explicitly associating the possible values of a named property with alternative schemas. @@ -3372,8 +3372,9 @@ Note that `discriminator` MUST NOT change the validation outcome of the schema. | Field Name | Type | Description | | ---- | :----: | ---- | -| propertyName | `string` | **REQUIRED**. The name of the property in the payload that will hold the discriminating value. This property SHOULD be required in the payload schema, as the behavior when the property is absent is undefined. | +| propertyName | `string` | **REQUIRED**. The name of the property in the payload that will hold the discriminating value. This property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `default` field that specifies which schema is expected to validate the structure of the model. | | mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or URI references. | +| default | `string` | The schema name or URI reference to a schema that is expected to validate the structure of the model when the `discriminator` property is not present in the payload. | This object MAY be extended with [Specification Extensions](#specification-extensions). @@ -3395,12 +3396,29 @@ The behavior of any configuration of `oneOf`, `anyOf`, `allOf` and `discriminato The value of the property named in `propertyName` is used as the name of the associated schema under the [Components Object](#components-object), _unless_ a `mapping` is present for that value. The `mapping` entry maps a specific property value to either a different schema component name, or to a schema identified by a URI. When using implicit or explicit schema component names, inline `oneOf` or `anyOf` subschemas are not considered. -The behavior of a `mapping` value that is both a valid schema name and a valid relative URI reference is implementation-defined, but it is RECOMMENDED that it be treated as a schema name. +The behavior of a `mapping` value or `default` value that is both a valid schema name and a valid relative URI reference is implementation-defined, but it is RECOMMENDED that it be treated as a schema name. To ensure that an ambiguous value (e.g. `"foo"`) is treated as a relative URI reference by all implementations, authors MUST prefix it with the `"."` path segment (e.g. `"./foo"`). Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison. However, the exact nature of such conversions are implementation-defined. +##### Optional `discriminator` property + +When the `discriminator` property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema that is expected to validate the structure of the model when the `discriminator` property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. + +The primary use case for an optional `discriminator` property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminator property. + +Typically the schema specified in the `default` field will specify that the `discriminator` property is not present, e.g. + +```yaml +OtherPet: + type: object + not: + required: ['petType'] +``` + +This will prevent the default schema from validating a payload that includes the `discriminator` property, which would cause a validation of the payload to fail when polymorphism is described using the `oneOf` JSON schema keyword. + ##### Examples For these examples, assume all schemas are in the [entry document](#openapi-description-structure) of the OAD; for handling of `discriminator` in referenced documents see [Resolving Implicit Connections](#resolving-implicit-connections). @@ -3458,6 +3476,28 @@ Here the discriminating value of `dog` will map to the schema `#/components/sche When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity for serializers/deserializers where multiple schemas may satisfy a single payload. +When the `discriminator` property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the `discriminator` property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. + +For example: + +```yaml +MyResponseType: + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Lizard' + - $ref: '#/components/schemas/OtherPet' + discriminator: + propertyName: petType + default: OtherPet +OtherPet: + type: object + not: + required: ['petType'] +``` + +In this example, if the `petType` property is not present in the payload, the payload should validate against the `OtherPet` schema. + This example shows the `allOf` usage, which avoids needing to reference all child schemas in the parent: ```yaml From 6c91a0b19e9e655f402db7ad60e344e5e0771343 Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Sun, 9 Feb 2025 10:14:50 -0600 Subject: [PATCH 2/9] Address comments from PR review --- src/oas.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/oas.md b/src/oas.md index 0d5d98083a..ccb4b6e92a 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2877,7 +2877,7 @@ These keywords can be used to describe polymorphism, where a single field can ac The OpenAPI specification extends the JSON Schema support for polymorphism by adding the [`discriminator`](#schema-discriminator) field. When used, the `discriminator` indicates the name of the property that hints which schema of an `anyOf` or `oneOf` is expected to validate the structure of the model. -The `discriminator` property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `default` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. +The discriminating property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `default` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. There are two ways to define the value of a discriminator for an inheriting instance. @@ -3372,9 +3372,9 @@ Note that `discriminator` MUST NOT change the validation outcome of the schema. | Field Name | Type | Description | | ---- | :----: | ---- | -| propertyName | `string` | **REQUIRED**. The name of the property in the payload that will hold the discriminating value. This property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `default` field that specifies which schema is expected to validate the structure of the model. | +| propertyName | `string` | **REQUIRED**. The name of the discriminating property in the payload that will hold the discriminating value. The discriminating property may be defined as required or optional, but when defined as optional the `discriminator` field must include a `default` field that specifies which schema is expected to validate the structure of the model. | | mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or URI references. | -| default | `string` | The schema name or URI reference to a schema that is expected to validate the structure of the model when the `discriminator` property is not present in the payload. | +| default | `string` | The schema name or URI reference to a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. | This object MAY be extended with [Specification Extensions](#specification-extensions). @@ -3402,13 +3402,13 @@ To ensure that an ambiguous value (e.g. `"foo"`) is treated as a relative URI re Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison. However, the exact nature of such conversions are implementation-defined. -##### Optional `discriminator` property +##### Optional discriminating property -When the `discriminator` property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema that is expected to validate the structure of the model when the `discriminator` property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. +When the discriminating property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. -The primary use case for an optional `discriminator` property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminator property. +The primary use case for an optional discriminating property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminator property. -Typically the schema specified in the `default` field will specify that the `discriminator` property is not present, e.g. +Typically the schema specified in the `default` field will specify that the discriminating property is not present, e.g. ```yaml OtherPet: @@ -3417,7 +3417,7 @@ OtherPet: required: ['petType'] ``` -This will prevent the default schema from validating a payload that includes the `discriminator` property, which would cause a validation of the payload to fail when polymorphism is described using the `oneOf` JSON schema keyword. +This will prevent the default schema from validating a payload that includes the discriminating property, which would cause a validation of the payload to fail when polymorphism is described using the `oneOf` JSON schema keyword. ##### Examples @@ -3476,7 +3476,7 @@ Here the discriminating value of `dog` will map to the schema `#/components/sche When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity for serializers/deserializers where multiple schemas may satisfy a single payload. -When the `discriminator` property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the `discriminator` property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. +When the discriminating property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. For example: From 9faf0311546ca5666fa488a83b2ccd3b42300ee3 Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Tue, 11 Feb 2025 08:31:55 -0600 Subject: [PATCH 3/9] Apply suggestions from PR review Co-authored-by: Jeremy Fiel <32110157+jeremyfiel@users.noreply.github.com> --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index ccb4b6e92a..6d83ae1dc7 100644 --- a/src/oas.md +++ b/src/oas.md @@ -3406,7 +3406,7 @@ However, the exact nature of such conversions are implementation-defined. When the discriminating property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. -The primary use case for an optional discriminating property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminator property. +The primary use case for an optional discriminating property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminating property. Typically the schema specified in the `default` field will specify that the discriminating property is not present, e.g. From 7ebde1428df16d1e007c913a8e838c3f3d340ace Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Tue, 11 Feb 2025 08:39:56 -0600 Subject: [PATCH 4/9] Use defaultMapping keyword in discriminator --- src/oas.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/oas.md b/src/oas.md index 6d83ae1dc7..287f9eb6d6 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2877,7 +2877,7 @@ These keywords can be used to describe polymorphism, where a single field can ac The OpenAPI specification extends the JSON Schema support for polymorphism by adding the [`discriminator`](#schema-discriminator) field. When used, the `discriminator` indicates the name of the property that hints which schema of an `anyOf` or `oneOf` is expected to validate the structure of the model. -The discriminating property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `default` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. +The discriminating property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `defaultMapping` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present. There are two ways to define the value of a discriminator for an inheriting instance. @@ -3372,9 +3372,9 @@ Note that `discriminator` MUST NOT change the validation outcome of the schema. | Field Name | Type | Description | | ---- | :----: | ---- | -| propertyName | `string` | **REQUIRED**. The name of the discriminating property in the payload that will hold the discriminating value. The discriminating property may be defined as required or optional, but when defined as optional the `discriminator` field must include a `default` field that specifies which schema is expected to validate the structure of the model. | +| propertyName | `string` | **REQUIRED**. The name of the discriminating property in the payload that will hold the discriminating value. The discriminating property may be defined as required or optional, but when defined as optional the `discriminator` field must include a `defaultMapping` field that specifies which schema is expected to validate the structure of the model when no discriminating property is present. | | mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or URI references. | -| default | `string` | The schema name or URI reference to a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. | +| defaultMapping | `string` | The schema name or URI reference to a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. | This object MAY be extended with [Specification Extensions](#specification-extensions). @@ -3404,11 +3404,11 @@ However, the exact nature of such conversions are implementation-defined. ##### Optional discriminating property -When the discriminating property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. +When the discriminating property is defined as optional, the `discriminator` field must include a `defaultMapping` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. The primary use case for an optional discriminating property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminating property. -Typically the schema specified in the `default` field will specify that the discriminating property is not present, e.g. +Typically the schema specified in the `defaultMapping` field will specify that the discriminating property is not present, e.g. ```yaml OtherPet: @@ -3417,7 +3417,7 @@ OtherPet: required: ['petType'] ``` -This will prevent the default schema from validating a payload that includes the discriminating property, which would cause a validation of the payload to fail when polymorphism is described using the `oneOf` JSON schema keyword. +This will prevent the default mapping schema from validating a payload that includes the discriminating property, which would cause a validation of the payload to fail when polymorphism is described using the `oneOf` JSON schema keyword. ##### Examples @@ -3476,7 +3476,7 @@ Here the discriminating value of `dog` will map to the schema `#/components/sche When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity for serializers/deserializers where multiple schemas may satisfy a single payload. -When the discriminating property is defined as optional, the `discriminator` field must include a `default` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. +When the discriminating property is defined as optional, the `discriminator` field must include a `defaultMapping` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. For example: @@ -3489,7 +3489,7 @@ MyResponseType: - $ref: '#/components/schemas/OtherPet' discriminator: propertyName: petType - default: OtherPet + defaultMapping: OtherPet OtherPet: type: object not: From 94700fd7e3fd4ac4fa1550cd6a5ab7eb7b468270 Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Fri, 14 Feb 2025 14:28:16 -0600 Subject: [PATCH 5/9] Prefer Discriminator Object terminology --- src/oas.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/oas.md b/src/oas.md index 287f9eb6d6..016f0d0e7e 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2875,11 +2875,11 @@ JSON Schema also provides the `anyOf` and `oneOf` keywords, which allow defining As is the case with `allOf`, the schemas are validated _independently_. These keywords can be used to describe polymorphism, where a single field can accept multiple types of values. -The OpenAPI specification extends the JSON Schema support for polymorphism by adding the [`discriminator`](#schema-discriminator) field. -When used, the `discriminator` indicates the name of the property that hints which schema of an `anyOf` or `oneOf` is expected to validate the structure of the model. -The discriminating property may be defined as required or optional, but when defined as an optional property the `discriminator` field must include a `defaultMapping` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present. +The OpenAPI specification extends the JSON Schema support for polymorphism by adding the [`discriminator`](#schema-discriminator) field whose value is a [Discriminator Object](#discriminator-object). +When used, the Discriminator Object indicates the name of the property that hints which schema of an `anyOf` or `oneOf` is expected to validate the structure of the model. +The discriminating property may be defined as required or optional, but when defined as an optional property the Discriminator Object must include a `defaultMapping` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present. -There are two ways to define the value of a discriminator for an inheriting instance. +There are two ways to define the value of a discriminating property for an inheriting instance. * Use the schema name. * [Override the schema name](#discriminator-mapping) by overriding the property with a new value. If a new value exists, this takes precedence over the schema name. @@ -3189,9 +3189,9 @@ components: - packSize ``` -###### Models with Polymorphism Support and a Discriminator field +###### Models with Polymorphism Support and a Discriminator Object -The following example extends the example of the previous section by adding a `discriminator` field to the `Pet` model. Note that the `discriminator` is only a hint to the consumer of the API, and does not change the validation outcome of the schema. +The following example extends the example of the previous section by adding a [Discriminator Object](#discriminator-object) to the `Pet` schema. Note that the Discriminator Object is only a hint to the consumer of the API, and does not change the validation outcome of the schema. ```yaml components: @@ -3362,7 +3362,7 @@ components: When request bodies or response payloads may be one of a number of different schemas, these should use the JSON Schema `anyOf` or `oneOf` keywords to describe the possible schemas (see [Composition and Inheritance](#composition-and-inheritance-polymorphism)). -A polymorphic schema MAY include a `discriminator` field, which defines the name of the property that may be used as a hint for which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. +A polymorphic schema MAY include a Discriminator Object, which defines the name of the property that may be used as a hint for which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. This hint can be used to aid in serialization, deserialization, and validation. The Discriminator Object does this by implicitly or explicitly associating the possible values of a named property with alternative schemas. @@ -3372,7 +3372,7 @@ Note that `discriminator` MUST NOT change the validation outcome of the schema. | Field Name | Type | Description | | ---- | :----: | ---- | -| propertyName | `string` | **REQUIRED**. The name of the discriminating property in the payload that will hold the discriminating value. The discriminating property may be defined as required or optional, but when defined as optional the `discriminator` field must include a `defaultMapping` field that specifies which schema is expected to validate the structure of the model when no discriminating property is present. | +| propertyName | `string` | **REQUIRED**. The name of the discriminating property in the payload that will hold the discriminating value. The discriminating property may be defined as required or optional, but when defined as optional the Discriminator Object must include a `defaultMapping` field that specifies which schema is expected to validate the structure of the model when no discriminating property is present. | | mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or URI references. | | defaultMapping | `string` | The schema name or URI reference to a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. | @@ -3404,7 +3404,7 @@ However, the exact nature of such conversions are implementation-defined. ##### Optional discriminating property -When the discriminating property is defined as optional, the `discriminator` field must include a `defaultMapping` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. +When the discriminating property is defined as optional, the [Discriminator Object](#discriminator-object) must include a `defaultMapping` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminating property is missing. The primary use case for an optional discriminating property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminating property. @@ -3433,7 +3433,7 @@ MyResponseType: - $ref: '#/components/schemas/Lizard' ``` -which means the payload _MUST_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. Deserialization of a `oneOf` can be a costly operation, as it requires determining which schema matches the payload and thus should be used in deserialization. This problem also exists for `anyOf` schemas. A `discriminator` MAY be used as a "hint" to improve the efficiency of selection of the matching schema. The `discriminator` field cannot change the validation result of the `oneOf`, it can only help make the deserialization more efficient and provide better error messaging. We can specify the exact field that tells us which schema is expected to match the instance: +which means the payload _MUST_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. Deserialization of a `oneOf` can be a costly operation, as it requires determining which schema matches the payload and thus should be used in deserialization. This problem also exists for `anyOf` schemas. A `discriminator` MAY be used as a "hint" to improve the efficiency of selection of the matching schema. The Discriminator Object cannot change the validation result of the `oneOf`, it can only help make the deserialization more efficient and provide better error messaging. We can specify the exact field that tells us which schema is expected to match the instance: ```yaml MyResponseType: @@ -3456,7 +3456,7 @@ The expectation now is that a property with name `petType` _MUST_ be present in will indicate that the `Cat` schema is expected to match this payload. -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: +In scenarios where the value of the discriminating property does not match the schema name or implicit mapping is not possible, an optional `mapping` definition MAY be used: ```yaml MyResponseType: @@ -3476,7 +3476,7 @@ Here the discriminating value of `dog` will map to the schema `#/components/sche When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity for serializers/deserializers where multiple schemas may satisfy a single payload. -When the discriminating property is defined as optional, the `discriminator` field must include a `defaultMapping` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. +When the discriminating property is defined as optional, the Discriminator Object must include a `defaultMapping` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. For example: From d36a8ffbfa44b751e413dee7d729e49befcc518d Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Fri, 14 Feb 2025 19:59:38 -0600 Subject: [PATCH 6/9] More updates for PR review comments --- src/oas.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/oas.md b/src/oas.md index 016f0d0e7e..02f9f3357d 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2877,7 +2877,7 @@ These keywords can be used to describe polymorphism, where a single field can ac The OpenAPI specification extends the JSON Schema support for polymorphism by adding the [`discriminator`](#schema-discriminator) field whose value is a [Discriminator Object](#discriminator-object). When used, the Discriminator Object indicates the name of the property that hints which schema of an `anyOf` or `oneOf` is expected to validate the structure of the model. -The discriminating property may be defined as required or optional, but when defined as an optional property the Discriminator Object must include a `defaultMapping` field that specifies which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present. +The discriminating property MAY be defined as required or optional, but when defined as an optional property the Discriminator Object MUST include a `defaultMapping` field that specifies which schema of the `anyOf` or `oneOf`, or which schema that references the current schema in an `allOf`, is expected to validate the structure of the model when the discriminating property is not present. There are two ways to define the value of a discriminating property for an inheriting instance. @@ -3362,7 +3362,7 @@ components: When request bodies or response payloads may be one of a number of different schemas, these should use the JSON Schema `anyOf` or `oneOf` keywords to describe the possible schemas (see [Composition and Inheritance](#composition-and-inheritance-polymorphism)). -A polymorphic schema MAY include a Discriminator Object, which defines the name of the property that may be used as a hint for which schema of the `anyOf` or `oneOf` is expected to validate the structure of the model. +A polymorphic schema MAY include a Discriminator Object, which defines the name of the property that may be used as a hint for which schema of the `anyOf` or `oneOf`, or which schema that references the current schema in an `allOf`, is expected to validate the structure of the model. This hint can be used to aid in serialization, deserialization, and validation. The Discriminator Object does this by implicitly or explicitly associating the possible values of a named property with alternative schemas. @@ -3372,7 +3372,7 @@ Note that `discriminator` MUST NOT change the validation outcome of the schema. | Field Name | Type | Description | | ---- | :----: | ---- | -| propertyName | `string` | **REQUIRED**. The name of the discriminating property in the payload that will hold the discriminating value. The discriminating property may be defined as required or optional, but when defined as optional the Discriminator Object must include a `defaultMapping` field that specifies which schema is expected to validate the structure of the model when no discriminating property is present. | +| propertyName | `string` | **REQUIRED**. The name of the discriminating property in the payload that will hold the discriminating value. The discriminating property MAY be defined as required or optional, but when defined as optional the Discriminator Object MUST include a `defaultMapping` field that specifies which schema is expected to validate the structure of the model when the discriminating property is not present. | | mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or URI references. | | defaultMapping | `string` | The schema name or URI reference to a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. | @@ -3396,7 +3396,7 @@ The behavior of any configuration of `oneOf`, `anyOf`, `allOf` and `discriminato The value of the property named in `propertyName` is used as the name of the associated schema under the [Components Object](#components-object), _unless_ a `mapping` is present for that value. The `mapping` entry maps a specific property value to either a different schema component name, or to a schema identified by a URI. When using implicit or explicit schema component names, inline `oneOf` or `anyOf` subschemas are not considered. -The behavior of a `mapping` value or `default` value that is both a valid schema name and a valid relative URI reference is implementation-defined, but it is RECOMMENDED that it be treated as a schema name. +The behavior of a `mapping` value or `defaultMapping` value that is both a valid schema name and a valid relative URI reference is implementation-defined, but it is RECOMMENDED that it be treated as a schema name. To ensure that an ambiguous value (e.g. `"foo"`) is treated as a relative URI reference by all implementations, authors MUST prefix it with the `"."` path segment (e.g. `"./foo"`). Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison. @@ -3404,7 +3404,7 @@ However, the exact nature of such conversions are implementation-defined. ##### Optional discriminating property -When the discriminating property is defined as optional, the [Discriminator Object](#discriminator-object) must include a `defaultMapping` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminating property is missing. +When the discriminating property is defined as optional, the [Discriminator Object](#discriminator-object) MUST include a `defaultMapping` field that specifies a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminating property is missing. The primary use case for an optional discriminating property is to allow a schema to be extended with a discriminator without breaking existing clients that do not provide the discriminating property. @@ -3476,7 +3476,7 @@ Here the discriminating value of `dog` will map to the schema `#/components/sche When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity for serializers/deserializers where multiple schemas may satisfy a single payload. -When the discriminating property is defined as optional, the Discriminator Object must include a `defaultMapping` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. +When the discriminating property is defined as optional, the Discriminator Object MUST include a `defaultMapping` field that specifies a schema of the `anyOf` or `oneOf` is expected to validate the structure of the model when the discriminating property is not present in the payload. This allows the schema to still be validated correctly even if the discriminator property is missing. For example: From 27216de196cd2992dbda2067340ca680b3cd70c8 Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Fri, 14 Feb 2025 20:19:00 -0600 Subject: [PATCH 7/9] Add back polymorphic example using allOf --- src/oas.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/oas.md b/src/oas.md index 02f9f3357d..a85c0835d5 100644 --- a/src/oas.md +++ b/src/oas.md @@ -3245,6 +3245,57 @@ components: - packSize ``` +###### Models with Polymorphism Support using allOf and a Discriminator Object + +It is also possible to describe polymorphic models using `allOf`. The following example uses `allOf` with a [Discriminator Object](#discriminator-object) to describe a polymorphic `Pet` model. + +```yaml +components: + schemas: + Pet: + type: object + discriminator: + propertyName: petType + properties: + name: + type: string + petType: + type: string + required: + - name + - petType + Cat: # "Cat" will be used as the discriminating value + description: A representation of a cat + allOf: + - $ref: '#/components/schemas/Pet' + - type: object + properties: + huntingSkill: + type: string + description: The measured skill for hunting + enum: + - clueless + - lazy + - adventurous + - aggressive + required: + - huntingSkill + Dog: # "Dog" will be used as the discriminating 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 +``` + ###### Generic Data Structure Model ```JSON From 3f82212b37fffd52d70c999c62d6052de1b9a1aa Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Mon, 17 Feb 2025 11:41:42 +0100 Subject: [PATCH 8/9] Apply suggestions from code review Co-authored-by: Jeremy Fiel <32110157+jeremyfiel@users.noreply.github.com> --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index a85c0835d5..fac5cc3ec8 100644 --- a/src/oas.md +++ b/src/oas.md @@ -3484,7 +3484,7 @@ MyResponseType: - $ref: '#/components/schemas/Lizard' ``` -which means the payload _MUST_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. Deserialization of a `oneOf` can be a costly operation, as it requires determining which schema matches the payload and thus should be used in deserialization. This problem also exists for `anyOf` schemas. A `discriminator` MAY be used as a "hint" to improve the efficiency of selection of the matching schema. The Discriminator Object cannot change the validation result of the `oneOf`, it can only help make the deserialization more efficient and provide better error messaging. We can specify the exact field that tells us which schema is expected to match the instance: +which means the payload _MUST_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. Deserialization of a `oneOf` can be a costly operation, as it requires determining which schema matches the payload and thus should be used in deserialization. This problem also exists for `anyOf` schemas. A `discriminator` MAY be used as a "hint" to improve the efficiency of selection of the matching schema. The [Discriminator Object](#discriminator-object) cannot change the validation result of the `oneOf`, it can only help make the deserialization more efficient and provide better error messaging. We can specify the exact field that tells us which schema is expected to match the instance: ```yaml MyResponseType: From 4018f807c449a19806ec218c82bbec2c14959a0c Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Mon, 17 Feb 2025 11:57:01 +0100 Subject: [PATCH 9/9] Update src/oas.md --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index fac5cc3ec8..574d571e67 100644 --- a/src/oas.md +++ b/src/oas.md @@ -3191,7 +3191,7 @@ components: ###### Models with Polymorphism Support and a Discriminator Object -The following example extends the example of the previous section by adding a [Discriminator Object](#discriminator-object) to the `Pet` schema. Note that the Discriminator Object is only a hint to the consumer of the API, and does not change the validation outcome of the schema. +The following example extends the example of the previous section by adding a [Discriminator Object](#discriminator-object) to the `Pet` schema. Note that the Discriminator Object is only a hint to the consumer of the API and does not change the validation outcome of the schema. ```yaml components: