-
Notifications
You must be signed in to change notification settings - Fork 9.2k
Allow discriminator to be optional #4339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
f1d5629
6c91a0b
9faf031
7ebde14
94700fd
d36a8ff
27216de
3f82212
4018f80
c11fda7
da3c386
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
||
|
@@ -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: | ||
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it valid (but duplicate) or illegal to also add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is covered in the example just above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do mean the combination of components:
schemas:
Pet:
type: object
discriminator:
propertyName: petType
properties:
name:
type: string
petType:
type: string
required:
- name
- petType
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hfhbd no, that is not valid in terms of JSON Schema (and therefore also invalid OAS). The reason is that this creates a cyclic reference that cannot be disambiguated by runtime evaluation. You can see this because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for your helpful answer and clarification! |
||
|
||
```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 | ||
|
@@ -3362,7 +3413,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,9 +3423,9 @@ Note that `discriminator` MUST NOT change the validation outcome of the schema. | |
|
||
| Field Name | Type | Description | | ||
| ---- | :----: | ---- | | ||
| <a name="property-name"></a>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. | | ||
| <a name="property-name"></a>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. | | ||
| <a name="discriminator-mapping"></a> mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or URI references. | | ||
| <a name="default"></a> 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. | | ||
| <a name="default"></a> 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 or contains a value for which there is no explicit or implicit mapping. | | ||
|
||
This object MAY be extended with [Specification Extensions](#specification-extensions). | ||
|
||
|
@@ -3396,28 +3447,32 @@ 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. | ||
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 or contains a value for which there is no explicit or implicit mapping. 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. | ||
|
||
Typically the schema specified in the `defaultMapping` field will specify that the discriminating property is not present, e.g. | ||
When the discriminating property is defined as optional, it is important that each subschema that defines a value for the discriminating property also define the property as required, since this is no longer enforced by the parent schema. | ||
|
||
The `defaultMapping` schema is also expected to validate the structure of the model when the discriminating property is present but contains a value for which there is no explicit or implicit mapping. This is typically expressed in the `defaultMapping` schema by excluding any instances with mapped values of the discriminating property, e.g. | ||
|
||
```yaml | ||
OtherPet: | ||
type: object | ||
not: | ||
required: ['petType'] | ||
properties: | ||
petType: | ||
not: | ||
enum: ['cat', 'dog'] | ||
``` | ||
|
||
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. | ||
This prevents the `defaultMapping` schema from validating a payload that includes the discriminating property with a mapped discriminating value, which would cause a validation to fail when polymorphism is described using the `oneOf` JSON schema keyword. | ||
|
||
##### Examples | ||
ralfhandl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
@@ -3433,7 +3488,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: | ||
mikekistler marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
```yaml | ||
MyResponseType: | ||
|
@@ -3476,7 +3531,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. | ||
mikekistler marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
For example: | ||
|
||
|
@@ -3492,11 +3547,13 @@ MyResponseType: | |
defaultMapping: OtherPet | ||
OtherPet: | ||
type: object | ||
not: | ||
required: ['petType'] | ||
properties: | ||
petType: | ||
not: | ||
enum: ['Cat', 'Dog', 'Lizard'] | ||
``` | ||
|
||
In this example, if the `petType` property is not present in the payload, the payload should validate against the `OtherPet` schema. | ||
In this example, if the `petType` property is not present in the payload, or if the value of `petType` is not "Cat", "Dog", or "Lizard", then 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: | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.