xref: /aosp_15_r20/external/json-schema-validator/doc/openapi-discriminators.md (revision 78c4dd6aa35290980cdcd1623a7e337e8d021c7c)
1[//]: # (Copyright 2021, Oracle and/or its affiliates.)
2
3## OpenAPI 3.x discriminator support
4
5Starting with `1.0.51`, `json-schema-validator` partly supports the use of the [`discriminator`](https://github.com/OAI/OpenAPI-Specification/blob/7cc8f4c4e742a20687fa65ace54ed32fcb8c6df0/versions/3.1.0.md#discriminator-object) keyword.
6
7Note that the use of the `discriminator` keyword does not affect the validation of `anyOf` or `oneOf`. The use of `discriminator` is not equivalent to having a `if`/`then` with the `discriminator` propertyName.
8
9When a `discriminator` is used, the assertions generated by `anyOf` or `oneOf` will only be the assertions generated from the schema that the discriminator applies to. An assertion will be generated if a `discriminator` is used but there is no matching schema that maps to the value in the `propertyName`.
10
11## How to use
12
131. Configure `SchemaValidatorsConfig` accordingly:
14   ```java
15   class Demo{
16     void demo() {
17        SchemaValidatorsConfig config = new SchemaValidatorsConfig();
18        config.setOpenAPI3StyleDiscriminators(true); // defaults to false
19     }
20   }
21   ```
222. Use the configured `SchemaValidatorsConfig` with the `JsonSchemaFactory` when creating the `JsonSchema`
23   ```java
24   class Demo{
25     void demo() {
26        SchemaValidatorsConfig config = new SchemaValidatorsConfig();
27        config.setOpenAPI3StyleDiscriminators(true); // defaults to false
28        JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
29        JsonSchema schema = factory.getSchema(schemaURI, schemaJacksonJsonNode, config);
30     }
31   }
32   ```
333. Ensure that the type field that you want to use as discriminator `propertyName` is required in your schema
34
35## Scope of Support
36
37Discriminators are unfortunately somewhat vague in their definition, especially in regard to JSON Schema validation. So, only
38those parts that are indisputable are considered at this moment.
39
40### Supported:
41
42* Polymorphism using `allOf` and `anyOf` with implicit and explicit `mapping`
43* `discriminator` on base types and types derived
44  thereof `A(with base discriminator) -> B(with optional additive discriminator) -> C(with optional additive discriminator)`
45
46### Not supported:
47
48* `propertyName` redefinition is prohibited on additive discriminators
49* `mapping` key redefinition is also prohibited on additive discriminators
50* the specification indicates that inline properties should be ignored.
51  So, this example would respect `foo`
52    ```yaml
53    allOf:
54        - $ref: otherSchema
55        - type: object
56          properties:
57            foo:
58            type: string
59          required: ["foo"]
60    ```
61  while
62    ```yaml
63    properties:
64      foo:
65        type: string
66    required: ["foo"]
67    allOf:
68      - $ref: otherSchema
69    ```
70  should ignore `foo`. **Ignoring `foo` in the second example is currently not implemented**
71* You won't get a warning if your `discriminator` uses a field for `propertyName` that is not `required`
72
73## Schema Examples
74
75More examples in https://github.com/networknt/json-schema-validator/blob/master/src/test/resources/openapi3/discriminator.json
76
77### Base type and extended type (the `anyOf` forward references are required)
78
79#### Example:
80
81```json
82{
83    "anyOf": [
84        {
85            "$ref": "#/components/schemas/Room"
86        },
87        {
88            "$ref": "#/components/schemas/BedRoom"
89        }
90    ],
91    "components": {
92        "schemas": {
93            "Room": {
94                "type": "object",
95                "properties": {
96                    "@type": {
97                        "type": "string"
98                    },
99                    "floor": {
100                        "type": "integer"
101                    }
102                },
103                "required": [
104                    "@type"
105                ],
106                "discriminator": {
107                    "propertyName": "@type"
108                }
109            },
110            "BedRoom": {
111                "type": "object",
112                "allOf": [
113                    {
114                        "$ref": "#/components/schemas/Room"
115                    },
116                    {
117                        "type": "object",
118                        "properties": {
119                            "numberOfBeds": {
120                                "type": "integer"
121                            }
122                        },
123                        "required": [
124                            "numberOfBeds"
125                        ]
126                    }
127                ]
128            }
129        }
130    }
131}
132```
133
134#### Here the default mapping key for `BedRoom` is overridden with `bed` from `Room`
135
136```json
137{
138    "anyOf": [
139        {
140            "$ref": "#/components/schemas/Room"
141        },
142        {
143            "$ref": "#/components/schemas/BedRoom"
144        }
145    ],
146    "components": {
147        "schemas": {
148            "Room": {
149                "type": "object",
150                "properties": {
151                    "@type": {
152                        "type": "string"
153                    },
154                    "floor": {
155                        "type": "integer"
156                    }
157                },
158                "required": [
159                    "@type"
160                ],
161                "discriminator": {
162                    "propertyName": "@type",
163                    "mapping": {
164                        "bed": "#/components/schemas/BedRoom"
165                    }
166                }
167            },
168            "BedRoom": {
169                "type": "object",
170                "allOf": [
171                    {
172                        "$ref": "#/components/schemas/Room"
173                    },
174                    {
175                        "type": "object",
176                        "properties": {
177                            "numberOfBeds": {
178                                "type": "integer"
179                            }
180                        },
181                        "required": [
182                            "numberOfBeds"
183                        ]
184                    }
185                ]
186            }
187        }
188    }
189}
190```
191