Search code examples
jsonschemaajv

JSON Schema array oneOf ref validates positive when it shouldn't


This is my schema. The goal is for the array's items to validate against the channel schema or the router schema.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$defs": {
    "channel": {
      "type": "object",
      "properties": {
        "channelType": {
          "type": "string",
          "enum": [
            "chat",
            "phone"
          ]
        }
      },
      "required": [
        "channelType"
      ]
    },
    "router": {
      "type": "object",
      "properties": {
        "contentType": {
          "type": "string",
          "const": "router"
        }
      },
      "required": [
        "contentType"
      ]
    }
  },
  "properties": {
    "items": {
      "type": "array",
      "items": [{
        "type": "object",
        "oneOf": [
          {
            "$ref": "#/$defs/router"
          },
          {
            "$ref": "#/$defs/channel"
          }
        ]
      }]
    }
  },
  "required": [
    "items"
  ]
}

I'm validating using ajv.js and doublechecking using https://www.jsonschemavalidator.net/

As expected, the following validates:

{
  "items": [
    {
      "contentType": "channel",
      "channelType": "phone"
    },
    {
      "contentType": "router"
    }
  ]
}

As expected, this does not validate:

{
  "items": [
    {
      "foo": true
    }
  ]
}

But this validates, and it shouldn't:

{
  "items": [
    {
      "contentType": "channel",
      "channelType": "phone"
    },
    {
      "contentType": "router"
    },
    {
      "foo": true
    }
  ]
}

I've tried different combinations of nesting and I'm clearly doing something wrong. Thanks!


Solution

  • Luckily, this is an easy one. The items keyword should be defined as an object schema, not an array.

    If you define it as an array, it will only validate the indexes which are defined. In your case, you only defined the first index of the items array. Thus, the validation only works on that index, all the other items in the array would be valid with any type, as you experienced.

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "$defs": {
        "channel": {
          "type": "object",
          "properties": {
            "channelType": {
              "type": "string",
              "enum": [
                "chat",
                "phone"
              ]
            }
          },
          "required": [
            "channelType"
          ]
        },
        "router": {
          "type": "object",
          "properties": {
            "contentType": {
              "type": "string",
              "const": "router"
            }
          },
          "required": [
            "contentType"
          ]
        }
      },
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "oneOf": [
              {
                "$ref": "#/$defs/router"
              },
              {
                "$ref": "#/$defs/channel"
              }
            ]
          }
        }
      },
      "required": [
        "items"
      ]
    }
    
    

    Btw, while using reserved JSON Schema words as properties is acceptable(items), it's not recommended because it will definitely confuse you when trying to troubleshoot your schemas.