Kamt Schatka
Kamt Schatka

Reputation: 57

How to improve JSON Schema validation results when using "anyOf"

We want to validate an array of elements with a JSON Schema Validator (https://github.com/networknt/json-schema-validator if that makes any difference).

Unfortunately we get quite ambiguous error messages if the validation fails and I am wondering if there is a way to improve this.

To make it clearer, I have created a small sample:

Each element in the array has a property "pet_type" which we can be sure to be there all the time (yeah I know that is discussable, but for the sake of argument let's ignore that for now). Each element has then some other properties. This is the json schema right now.
Envelope (Basically with an array of elements)

{
  "title": "Envelope",
  "type": "object",
  "properties": {
    "pets": {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "$ref": "./Cat.json"
          },
          {
            "$ref": "./Dog.json"
          }
        ]
      }
    }
  }
}

Cat (one of the elements in the array)

{
  "title": "Cat",
  "allOf": [
    {
      "$ref": "Pet.json"
    },
    {
      "type": "object",
      "properties": {
        "hunts": {
          "type": "boolean"
        },
        "age": {
          "type": "integer"
        },
        "pet_type": {
          "type": "string",
          "enum": [
            "Cat"
          ],
          "pattern": "Cat"
        }
      },
      "required": [
        "pet_type",
        "age"
      ]
    }
  ]
}

Dog:

{
  "title": "Dog",
  "allOf": [
    {
      "$ref": "Pet.json"
    },
    {
      "type": "object",
      "properties": {
        "bark": {
          "type": "boolean"
        },
        "breed": {
          "type": "string"
        },
        "pet_type": {
          "type": "string",
          "enum": [
            "Dog"
          ],
          "pattern": "Dog"
        }
      },
      "required": [
        "bark"
      ]
    }
  ]
}

And the Pet.json, which specifies that "pet_type" is required on each of the elements.

{
  "title": "Pet",
  "type": "object",
  "discriminator": {
    "propertyName": "pet_type"
  },
  "properties": {
    "pet_type": {
      "type": "string",
      "enum": [
        "Cat",
        "Dog"
      ]
    }
  },
  "required": [
    "pet_type"
  ]
}

The problem I have is that the error messages get very long, because the element is validated against all of the constraints, without narrowing it down a bit.

Let's say the JSON to validate looks like this:

{
  "pets": [
    {
      "pet_type": "Cat",
      "hunts": true             
    }
  ]
}

"pet_type" is set to "Cat", so for a human it is "clear" that it should only validate against the Cat JSON Schema and then show that "age" is missing.

What really happens is that there are 4 violations:

Note: I have added the enum/regex pattern in an act of trying to change the behavior, but it did not work.

I understand from a technical standpoint why it behaves like it does, I just want to know if it is possible to tell the validator somehow to first narrow down the validation based on pet_type and then keep validating?

What I tried to get it working:

{
  "title": "Envelope",
  "type": "object",
  "properties": {
    "pets": {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "if": {
              "properties": {
                "pet_type": {
                  "const": "Cat"
                }
              }
            },
            "then": {
              "$ref": "./Cat.json"
            }
          },
          {
            "if": {
              "properties": {
                "pet_type": {
                  "const": "Dog"
                }
              }
            },
            "then": {
              "$ref": "./Dog.json"
            }
          }
        ]
      }
    }
  }
}

My sample I tried to validate gives no errors anymore:

{
  "pets": [
    {
      "pet_type": "Cat",
      "hunts": true             
    }
  ]
}

Upvotes: 1

Views: 230

Answers (1)

Kamt Schatka
Kamt Schatka

Reputation: 57

Turns out the solution with if/then was correct, and the json-schema-validator actually detects the problems, but has some code that should improve the error messages, but gets rid of the validation errors instead. I will bring the problem to their attention.

For now I have found a workaround to use nested if/then/else statements:

{
    "title": "Envelope",
    "type": "object",
    "properties": {
        "pets": {
            "type": "array",
            "items": {
                "if": {
                    "properties": {
                        "pet_type": {
                            "const": "Cat"
                        }
                    }
                },
                "then": {
                    "$ref": "./Cat.json"
                },
                "else": {
                    "if": {
                        "properties": {
                            "pet_type": {
                                "const": "Dog"
                            }
                        }
                    },
                    "then": {
                        "$ref": "./Dog.json"
                    },
                    "else": {
                        "properties": {
                            "pet_type": {
                                "type": "string",
                                "enum": [
                                    "Cat",
                                    "Dog"
                                ]
                            }
                        }
                    }
                }
            }
        }
    },
    "additionalProperties": false
}

Upvotes: 0

Related Questions