user2993021
user2993021

Reputation: 87

JSON Schema - Allow only specific enum values for a property and reject the rest

Say I have the following JSON that I'd like validated.

[
   {
      "UpStatus":"Closed"
   },
   {
      "UpStatus":"Open"
   }
]

I want the json to pass validation only if there is at least one 'UpStatus' in the array defined to either 'Open' or 'Locked'.

If 'UpStatus' is not found as set to 'Open' or 'Locked' in the array, and is set to something else that is arbitrary say "Closed", I want the validation to fail.

I tinkered around with anyOf and came up with the following schema.

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "array",
  "items": [
    {
      "type": "object",
      "properties": {
        "UpStatus": {
          "type": "string"
        }
      },
      "minItems": 1,
      "anyOf": [
        {
          "properties": {
            "UpStatus": {
              "const": "Open"
            }
          },
          "required": [
            "UpStatus"
          ]
        },
        {
          "properties": {
            "UpStatus": {
              "const": "Locked"
            }
          },
          "required": [
            "UpStatus"
          ]
        }
      ]
    }
  ]
}

The above does not work correctly as it allows the following to pass which I thought it should fail to validate.

[
  {
    "UpStatus": "Closed"
  },
  {
    "UpStatus": "Closed"
  }
]

I played with the json schema for a long time and looked at examples and read some docs but could not get it to work. Any help is appreciated. Thank you.

Upvotes: 2

Views: 5044

Answers (1)

Ether
Ether

Reputation: 53996

In your schema above, you put the "minItems" keyword inside "items", which does nothing -- it needs to be adjacent to "items". But using "items" also means that all items must match, not just one.

Instead, use "contains":

{
  "type: "array",
  "contains": {
    "type": "object",
    "required": ["UpStatus"],
    "properties": {
      "UpStatus": {
        "enum": ["Open","Locked"],
      }
    }
  }
}

Translation: the data must be an array, where at least one element must be an object, which has the property "UpStatus" with value either "Open" or "Locked".

You may want all items in the array to conform to something specific, in which case you use "items" to specify that. The difference between "items" and "contains" is that the "items" schema must match all items, whereas the "contains" schema only has to match one.

HOWEVER, "contains" is not available in the draft 4 version of the spec. Is there any chance you can upgrade? There is a list of implementations in various languages here. Alternatively, you can simulate the "contains" keyword with "not": { "items": { "not": { ... schema ... } } } (courtesy Jason Desrosiers).


addendum: When I evaluate your schema and data, it does not pass, but rather produces these errors, so perhaps your implementation is buggy (or you mispasted something):

{
  "errors" : [
    {
      "error" : "value does not match",
      "instanceLocation" : "/0/UpStatus",
      "keywordLocation" : "/items/0/anyOf/0/properties/UpStatus/const"
    },
    {
      "error" : "not all properties are valid",
      "instanceLocation" : "/0",
      "keywordLocation" : "/items/0/anyOf/0/properties"
    },
    {
      "error" : "value does not match",
      "instanceLocation" : "/0/UpStatus",
      "keywordLocation" : "/items/0/anyOf/1/properties/UpStatus/const"
    },
    {
      "error" : "not all properties are valid",
      "instanceLocation" : "/0",
      "keywordLocation" : "/items/0/anyOf/1/properties"
    },
    {
      "error" : "no subschemas are valid",
      "instanceLocation" : "/0",
      "keywordLocation" : "/items/0/anyOf"
    },
    {
      "error" : "not all items are valid",
      "instanceLocation" : "",
      "keywordLocation" : "/items"
    }
  ],
  "valid" : false
}

Upvotes: 4

Related Questions