[//]: # (Copyright 2021, Oracle and/or its affiliates.)

## OpenAPI 3.x discriminator support

Starting 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.

Note 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. 

When 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`.

## How to use

1. Configure `SchemaValidatorsConfig` accordingly:
   ```java
   class Demo{ 
     void demo() {
        SchemaValidatorsConfig config = new SchemaValidatorsConfig();
        config.setOpenAPI3StyleDiscriminators(true); // defaults to false
     }
   }
   ```
2. Use the configured `SchemaValidatorsConfig` with the `JsonSchemaFactory` when creating the `JsonSchema`
   ```java
   class Demo{ 
     void demo() {
        SchemaValidatorsConfig config = new SchemaValidatorsConfig();
        config.setOpenAPI3StyleDiscriminators(true); // defaults to false
        JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
        JsonSchema schema = factory.getSchema(schemaURI, schemaJacksonJsonNode, config);
     }
   }
   ```
3. Ensure that the type field that you want to use as discriminator `propertyName` is required in your schema

## Scope of Support

Discriminators are unfortunately somewhat vague in their definition, especially in regard to JSON Schema validation. So, only
those parts that are indisputable are considered at this moment.

### Supported:

* Polymorphism using `allOf` and `anyOf` with implicit and explicit `mapping`
* `discriminator` on base types and types derived
  thereof `A(with base discriminator) -> B(with optional additive discriminator) -> C(with optional additive discriminator)`

### Not supported:

* `propertyName` redefinition is prohibited on additive discriminators
* `mapping` key redefinition is also prohibited on additive discriminators
* the specification indicates that inline properties should be ignored.
  So, this example would respect `foo`
    ```yaml
    allOf:
        - $ref: otherSchema
        - type: object
          properties:
            foo:
            type: string
          required: ["foo"]
    ```
  while
    ```yaml
    properties:
      foo:
        type: string
    required: ["foo"]
    allOf:
      - $ref: otherSchema
    ```
  should ignore `foo`. **Ignoring `foo` in the second example is currently not implemented**
* You won't get a warning if your `discriminator` uses a field for `propertyName` that is not `required`

## Schema Examples

More examples in https://github.com/networknt/json-schema-validator/blob/master/src/test/resources/openapi3/discriminator.json

### Base type and extended type (the `anyOf` forward references are required)

#### Example:

```json
{
    "anyOf": [
        {
            "$ref": "#/components/schemas/Room"
        },
        {
            "$ref": "#/components/schemas/BedRoom"
        }
    ],
    "components": {
        "schemas": {
            "Room": {
                "type": "object",
                "properties": {
                    "@type": {
                        "type": "string"
                    },
                    "floor": {
                        "type": "integer"
                    }
                },
                "required": [
                    "@type"
                ],
                "discriminator": {
                    "propertyName": "@type"
                }
            },
            "BedRoom": {
                "type": "object",
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Room"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "numberOfBeds": {
                                "type": "integer"
                            }
                        },
                        "required": [
                            "numberOfBeds"
                        ]
                    }
                ]
            }
        }
    }
}
```

#### Here the default mapping key for `BedRoom` is overridden with `bed` from `Room`

```json
{
    "anyOf": [
        {
            "$ref": "#/components/schemas/Room"
        },
        {
            "$ref": "#/components/schemas/BedRoom"
        }
    ],
    "components": {
        "schemas": {
            "Room": {
                "type": "object",
                "properties": {
                    "@type": {
                        "type": "string"
                    },
                    "floor": {
                        "type": "integer"
                    }
                },
                "required": [
                    "@type"
                ],
                "discriminator": {
                    "propertyName": "@type",
                    "mapping": {
                        "bed": "#/components/schemas/BedRoom"
                    }
                }
            },
            "BedRoom": {
                "type": "object",
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Room"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "numberOfBeds": {
                                "type": "integer"
                            }
                        },
                        "required": [
                            "numberOfBeds"
                        ]
                    }
                ]
            }
        }
    }
}
```
