validating against a JSON schema containing multiple schema definitions

Viewed 1000

I'm creating a JSON schema for use in Postman that has a few schema definitions in them. The JSON I validate against the schema needs to match with one of the schema definitions.

SAMPLE SCHEMA

{
  "oneOf": [
    {
      "$ref": "#/definitions/schema1"
    },
    {
      "$ref": "#/definitions/schema2"
    },
    {
      "$ref": "#/definitions/schema3"
    }
  ],
  "definitions": {
    "schema1": {
      "type": "object",
      "properties": {
        "propertyA": {
          "type": "string"
        }
      },
      "required": [
        "propertyA"
      ]
    },
    "schema2": {
      "type": "object",
      "properties": {
        "propertyB": {
          "type": "string"
        }
      },
      "required": [
        "propertyB"
      ]
    },
    "schema3": {
      "type": "object",
      "properties": {
        "propertyC": {
          "type": "string"
        }
      },
      "required": [
        "propertyC"
      ]
    }
  }
}

SAMPLE JSON DATA

This JSON gets validated against the schema and correctly marked as invalid (because a string is needed):

{
    "propertyA": 123
}

THE PROBLEM

This example returns 4 errors according to https://www.jsonschemavalidator.net/:

  • Message: JSON is valid against no schemas from 'oneOf'. Schema path: #/oneOf
  • Message: Invalid type. Expected String but got Integer. Schema path: #/definitions/schema1/properties/propertyA/type
  • Message: Required properties are missing from object: propertyC.
    Schema path: #/definitions/schema3/required
  • Message: Required properties are missing from object: propertyB.
    Schema path: #/definitions/schema2/required

I'm only interested in the error message saying that it expected a String. How do I avoid those other error messages while keeping the schema definitions in 1 single file?

1 Answers

Yep, oneOf is terrible for this kind of thing. if/then is verbose, but gives you much better results. Basically, you need to identify some condition that determines whether the schema should apply to the instance.

Usually, that condition is the value of a common field. In this case, if the instance being validated is an object with "type": "A" then it has to validate against the /definitions/a schema. If it has "type": "B" then it has to validate against the /definitions/b schema.

{
  "allOf": [
    {
      "if": {
        "properties": {
          "type": { "const": "A" }
        },
        "required": ["type"]
      },
      "then": { "$ref": "#/definitions/a" }
    },
    {
      "if": {
        "properties": {
          "type": { "const": "B" }
        },
        "required": ["type"]
      },
      "then": { "$ref": "#/definitions/b" }
    }
  ]
}

If your condition is the presence of a specific field, you can use the dependencies keyword as a shortcut. If the instance being validated is an object with a "propertyA" property, then the instance must be valid against the /definitions/a schema. Likewise for "propertyB".

{
  "dependencies": {
    "propertyA": { "$ref": "#/definitions/a" },
    "propertyB": { "$ref": "#/definitions/b" }
  }
}

There's actually a super simple solution for your example, but I answered for the general case because I assume your actual schema is more complicated than the example.

{
  "type": "object",
  "properties": {
    "propertyA": { "type": "string" },
    "propertyB": { "type": "string" },
    "propertyC": { "type": "string" }
  },
  "oneOf": [
    { "required": ["propertyA"] },
    { "required": ["propertyB"] },
    { "required": ["propertyC"] }
  ]
}
Related