Skip to content

Commit f1d5629

Browse files
committed
First draft of optional discriminator feature
1 parent 0571af7 commit f1d5629

File tree

1 file changed

+141
-101
lines changed

1 file changed

+141
-101
lines changed

src/oas.md

Lines changed: 141 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -2832,7 +2832,7 @@ JSON Schema implementations MAY choose to treat keywords defined by the OpenAPI
28322832

28332833
| Field Name | Type | Description |
28342834
| ---- | :----: | ---- |
2835-
| <a name="schema-discriminator"></a>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. |
2835+
| <a name="schema-discriminator"></a>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. |
28362836
| <a name="schema-xml"></a>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. |
28372837
| <a name="schema-external-docs"></a>externalDocs | [External Documentation Object](#external-documentation-object) | Additional external documentation for this schema. |
28382838
| <a name="schema-example"></a>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.<br><br>**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
28702870
`allOf` takes an array of object definitions that are validated _independently_ but together compose a single object.
28712871

28722872
While composition offers model extensibility, it does not imply a hierarchy between the models.
2873-
To support polymorphism, the OpenAPI Specification adds the [`discriminator`](#schema-discriminator) field.
2874-
When used, the `discriminator` indicates the name of the property that hints which schema definition is expected to validate the structure of the model.
2875-
As such, the `discriminator` field MUST be a required field.
2873+
2874+
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.
2875+
As is the case with `allOf`, the schemas are validated _independently_.
2876+
These keywords can be used to describe polymorphism, where a single field can accept multiple types of values.
2877+
2878+
The OpenAPI specification extends the JSON Schema support for polymorphism by adding the [`discriminator`](#schema-discriminator) field.
2879+
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.
2880+
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.
2881+
28762882
There are two ways to define the value of a discriminator for an inheriting instance.
28772883

28782884
* Use the schema name.
@@ -3135,116 +3141,108 @@ components:
31353141

31363142
###### Models with Polymorphism Support
31373143

3138-
```json
3139-
{
3140-
"components": {
3141-
"schemas": {
3142-
"Pet": {
3143-
"type": "object",
3144-
"discriminator": {
3145-
"propertyName": "petType"
3146-
},
3147-
"properties": {
3148-
"name": {
3149-
"type": "string"
3150-
},
3151-
"petType": {
3152-
"type": "string"
3153-
}
3154-
},
3155-
"required": ["name", "petType"]
3156-
},
3157-
"Cat": {
3158-
"description": "A representation of a cat. Note that `Cat` will be used as the discriminating value.",
3159-
"allOf": [
3160-
{
3161-
"$ref": "#/components/schemas/Pet"
3162-
},
3163-
{
3164-
"type": "object",
3165-
"properties": {
3166-
"huntingSkill": {
3167-
"type": "string",
3168-
"description": "The measured skill for hunting",
3169-
"default": "lazy",
3170-
"enum": ["clueless", "lazy", "adventurous", "aggressive"]
3171-
}
3172-
},
3173-
"required": ["huntingSkill"]
3174-
}
3175-
]
3176-
},
3177-
"Dog": {
3178-
"description": "A representation of a dog. Note that `Dog` will be used as the discriminating value.",
3179-
"allOf": [
3180-
{
3181-
"$ref": "#/components/schemas/Pet"
3182-
},
3183-
{
3184-
"type": "object",
3185-
"properties": {
3186-
"packSize": {
3187-
"type": "integer",
3188-
"format": "int32",
3189-
"description": "the size of the pack the dog is from",
3190-
"default": 0,
3191-
"minimum": 0
3192-
}
3193-
},
3194-
"required": ["packSize"]
3195-
}
3196-
]
3197-
}
3198-
}
3199-
}
3200-
}
3144+
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.
3145+
3146+
```yaml
3147+
components:
3148+
schemas:
3149+
Pet:
3150+
type: object
3151+
properties:
3152+
name:
3153+
type: string
3154+
required:
3155+
- name
3156+
- petType
3157+
oneOf:
3158+
- $ref: '#/components/schemas/Cat'
3159+
- $ref: '#/components/schemas/Dog'
3160+
Cat:
3161+
description: A pet cat
3162+
type: object
3163+
properties:
3164+
petType:
3165+
const: 'cat'
3166+
huntingSkill:
3167+
type: string
3168+
description: The measured skill for hunting
3169+
enum:
3170+
- clueless
3171+
- lazy
3172+
- adventurous
3173+
- aggressive
3174+
required:
3175+
- huntingSkill
3176+
Dog:
3177+
description: A pet dog
3178+
type: object
3179+
properties:
3180+
petType:
3181+
const: 'dog'
3182+
packSize:
3183+
type: integer
3184+
format: int32
3185+
description: the size of the pack the dog is from
3186+
default: 0
3187+
minimum: 0
3188+
required:
3189+
- packSize
32013190
```
32023191

3192+
###### Models with Polymorphism Support and a Discriminator field
3193+
3194+
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.
3195+
32033196
```yaml
32043197
components:
32053198
schemas:
32063199
Pet:
32073200
type: object
32083201
discriminator:
32093202
propertyName: petType
3203+
mapping:
3204+
cat: '#/components/schemas/Cat'
3205+
dog: '#/components/schemas/Dog'
32103206
properties:
32113207
name:
32123208
type: string
3209+
required:
3210+
- name
3211+
- petType
3212+
oneOf:
3213+
- $ref: '#/components/schemas/Cat'
3214+
- $ref: '#/components/schemas/Dog'
3215+
Cat:
3216+
description: A pet cat
3217+
type: object
3218+
properties:
32133219
petType:
3220+
const: 'cat'
3221+
huntingSkill:
32143222
type: string
3223+
description: The measured skill for hunting
3224+
enum:
3225+
- clueless
3226+
- lazy
3227+
- adventurous
3228+
- aggressive
3229+
required:
3230+
- huntingSkill
3231+
Dog:
3232+
description: A pet dog
3233+
type: object
3234+
properties:
3235+
petType:
3236+
const: 'dog'
3237+
packSize:
3238+
type: integer
3239+
format: int32
3240+
description: the size of the pack the dog is from
3241+
default: 0
3242+
minimum: 0
32153243
required:
3216-
- name
32173244
- petType
3218-
Cat: # "Cat" will be used as the discriminating value
3219-
description: A representation of a cat
3220-
allOf:
3221-
- $ref: '#/components/schemas/Pet'
3222-
- type: object
3223-
properties:
3224-
huntingSkill:
3225-
type: string
3226-
description: The measured skill for hunting
3227-
enum:
3228-
- clueless
3229-
- lazy
3230-
- adventurous
3231-
- aggressive
3232-
required:
3233-
- huntingSkill
3234-
Dog: # "Dog" will be used as the discriminating value
3235-
description: A representation of a dog
3236-
allOf:
3237-
- $ref: '#/components/schemas/Pet'
3238-
- type: object
3239-
properties:
3240-
packSize:
3241-
type: integer
3242-
format: int32
3243-
description: the size of the pack the dog is from
3244-
default: 0
3245-
minimum: 0
3246-
required:
3247-
- packSize
3245+
- packSize
32483246
```
32493247

32503248
###### Generic Data Structure Model
@@ -3362,7 +3360,9 @@ components:
33623360

33633361
#### Discriminator Object
33643362

3365-
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.
3363+
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)).
3364+
3365+
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.
33663366
This hint can be used to aid in serialization, deserialization, and validation.
33673367
The Discriminator Object does this by implicitly or explicitly associating the possible values of a named property with alternative schemas.
33683368

@@ -3372,8 +3372,9 @@ Note that `discriminator` MUST NOT change the validation outcome of the schema.
33723372

33733373
| Field Name | Type | Description |
33743374
| ---- | :----: | ---- |
3375-
| <a name="property-name"></a>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. |
3375+
| <a name="property-name"></a>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. |
33763376
| <a name="discriminator-mapping"></a> mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or URI references. |
3377+
| <a name="default"></a> 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. |
33773378

33783379
This object MAY be extended with [Specification Extensions](#specification-extensions).
33793380

@@ -3395,12 +3396,29 @@ The behavior of any configuration of `oneOf`, `anyOf`, `allOf` and `discriminato
33953396
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.
33963397
The `mapping` entry maps a specific property value to either a different schema component name, or to a schema identified by a URI.
33973398
When using implicit or explicit schema component names, inline `oneOf` or `anyOf` subschemas are not considered.
3398-
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.
3399+
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.
33993400
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"`).
34003401

34013402
Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison.
34023403
However, the exact nature of such conversions are implementation-defined.
34033404

3405+
##### Optional `discriminator` property
3406+
3407+
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.
3408+
3409+
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.
3410+
3411+
Typically the schema specified in the `default` field will specify that the `discriminator` property is not present, e.g.
3412+
3413+
```yaml
3414+
OtherPet:
3415+
type: object
3416+
not:
3417+
required: ['petType']
3418+
```
3419+
3420+
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.
3421+
34043422
##### Examples
34053423

34063424
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
34583476

34593477
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.
34603478

3479+
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.
3480+
3481+
For example:
3482+
3483+
```yaml
3484+
MyResponseType:
3485+
oneOf:
3486+
- $ref: '#/components/schemas/Cat'
3487+
- $ref: '#/components/schemas/Dog'
3488+
- $ref: '#/components/schemas/Lizard'
3489+
- $ref: '#/components/schemas/OtherPet'
3490+
discriminator:
3491+
propertyName: petType
3492+
default: OtherPet
3493+
OtherPet:
3494+
type: object
3495+
not:
3496+
required: ['petType']
3497+
```
3498+
3499+
In this example, if the `petType` property is not present in the payload, the payload should validate against the `OtherPet` schema.
3500+
34613501
This example shows the `allOf` usage, which avoids needing to reference all child schemas in the parent:
34623502

34633503
```yaml

0 commit comments

Comments
 (0)