json-schema-validator icon indicating copy to clipboard operation
json-schema-validator copied to clipboard

Discriminator not working with arrays

Open cbornet opened this issue 3 years ago • 1 comments

It seems that when the anyOf is an array item, the validation is correct for the first item but for the remaining items, it validates against the first schema of the anyOf instead of using the discriminator.

To reproduce:

  • schema.json
{
  "type": "array",
  "items": {
    "anyOf": [
      {
        "$ref": "#/components/schemas/Kitchen"
      },
      {
        "$ref": "#/components/schemas/BedRoom"
      }
    ]
  },
  "components": {
    "schemas": {
      "Room": {
        "type": "object",
        "properties": {
          "@type": {
            "type": "string"
          }
        },
        "required": [
          "@type"
        ],
        "discriminator": {
          "propertyName": "@type"
        }
      },
      "BedRoom": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/Room"
          },
          {
            "type": "object",
            "properties": {
              "numberOfBeds": {
                "type": "integer"
              }
            },
            "required": [
              "numberOfBeds"
            ]
          }
        ]
      },
      "Kitchen": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/Room"
          },
          {
            "type": "object",
            "properties": {
              "hasMicrowaveOven": {
                "type": "boolean"
              }
            },
            "required": [
              "hasMicrowaveOven"
            ]
          }
        ]
      }
    }
  }
}
  • input.json
[
  {
    "@type": "Kitchen",
    "hasMicrowaveOven": true
  },
  {
    "@type": "BedRoom",
    "numberOfBeds": 4
  }
]
  • test case
  @Test
  void test() throws Exception {
    JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
    InputStream schemaStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("schema.json");
    InputStream jsonStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("input.json");
    SchemaValidatorsConfig config = new SchemaValidatorsConfig();
    config.setOpenAPI3StyleDiscriminators(true);
    JsonSchema schema = factory.getSchema(schemaStream, config);
    JsonNode jsonNode = new ObjectMapper().readTree(jsonStream);
    Set<ValidationMessage> errors = schema.validate(jsonNode);

    assertEquals(errors.size(), 0);
  }

Note: if we reverse order of the kitchen and the bedroom in the json input, then the test passes.

cbornet avatar Sep 15 '22 11:09 cbornet

@cbornet Thanks for open this issue. I will put your test case in and try to replicate and fix it.

stevehu avatar Sep 16 '22 15:09 stevehu

I believe we've hit this issue too. In the structure we have:

supplies:
  type: array
  items:
    oneOf:
      - $ref: '#/components/schemas/Gas'
      - $ref: '#/components/schemas/Electricity'


Supply:
  type: object
  required:
    - energyType
  properties:
    energyType:
      type: string
  discriminator:
    propertyName: energyType
    mapping:
      electricity: '#/components/schemas/Gas'
      gas: '#/components/schemas/Electricity'


Gas:
  allOf:
    - $ref: '#/components/schemas/Supply'
    - type: object
      required:
.....gas specific items

Electricity:
  allOf:
    - $ref: '#/components/schemas/Supply'
    - type: object
      required:
.....electricity specific items

energyType is what we use to determine if it's "Gas" or "Electricity". The two schemas are different but share "Supply". It will only validate against the "Gas" schema. Furthermore it also doesn't capture if I enter "water" or anything else other than "Gas" or "Electricity". Instead it will just pass through. What I see in the logs though is:

2022-12-09 23:32:46.571  WARN 15504 --- [nio-9010-exec-6] com.networknt.schema.JsonMetaSchema      : Unknown keyword components - you should define your own Meta Schema. If the keyword is irrelevant for validation, just use a NonValidationKeyword
2022-12-09 23:32:46.573  WARN 15504 --- [nio-9010-exec-6] com.networknt.schema.JsonMetaSchema      : Unknown keyword example - you should define your own Meta Schema. If the keyword is irrelevant for validation, just use a NonValidationKeyword
2022-12-09 23:32:46.578  WARN 15504 --- [nio-9010-exec-6] com.networknt.schema.JsonMetaSchema      : Unknown keyword discriminator - you should define your own Meta Schema. If the keyword is irrelevant for validation, just use a NonValidationKeyword

I'm under the impression that in my example "gas" or "electricity" should be provided to validate against their retrospective schema. Any other value should fail validation:

https://spec.openapis.org/oas/v3.1.0#discriminator-object

If the discriminator value does not match an implicit or explicit mapping, no schema can be determined and validation SHOULD fail.

I'm pretty new to this so happy to be corrected but wanted to share what I'd found - hopefully it's useful.

graemeberry-esgglobal avatar Dec 10 '22 14:12 graemeberry-esgglobal