2

I have a use case where I want to check the keys present in JSON, depending on the value of a different key.

Example JSON-1:

{ 
  "key_name" : "value1",
  "foo" : "random_value1"
}

Example JSON-2:

{ 
  "key_name" : "value2",
  "bar" : "random_value2"
}

As per these examples,

Rule 1. If the value of "key_name" is "value1" then only "foo" key should be present in JSON.

Rule 2. If the value of "key_name" is "value2", then only "bar" key should be present in JSON.

I have written the following JSON Schema for validating these JSON:

{
  "type": "object",
  "properties": {
    "key_name": {
      "type": "string",
      "enum": [
        "value1",
        "value2"
      ]
    },
    "foo": {
      "type": "string"
    },
    "bar": {
      "type": "string"
    }
  },
  "required": [
    "key_name"
  ],
  "additionalProperties": false,
  "allOf": [
    {
      "if": {
        "properties": {
          "key_name": {
            "enum": [
              "value1"
            ]
          }
        }
      },
      "then": {
        "required": [
          "foo"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "key_name": {
            "enum": [
              "value2"
            ]
          }
        }
      },
      "then": {
        "required": [
          "bar"
        ]
      }
    }
  ]
}

Now, as per the rules, the following JSON's are invalid, and should raise an error.

{ 
  "key_name" : "value1",
  "foo" : "random_value1",
  "bar" : "random_value2"
}

OR

{ 
  "key_name" : "value2",
  "bar" : "random_value2",
  "foo" : "random_value"
}

But, the above JSON Schema fails to do so. It only checks whether "foo"/"bar" key or not, as per the value of "key_name". It fails to check for existence of any new key.

How to go about it?

1 Answers1

1

This was already answered here: Mutually exclusive property groups.

Additionally, you can find a great overview here: jsonSchema attribute conditionally required.


For your specific examples, the following approaches come to mind:

  1. Add "not": { "required": ["bar"] } to your first then clause to indicate that "bar" is not allowed. And the same for "foo" in the second then clause then.
  2. If there is always just "key_name" and one other property allowed, you could also simply add "maxProperties": 2 in the main schema.

EDIT (to address whitelisting alternative):

Another option would be to define each permutation separately like this:

{
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "key_name": { "const": "value1" },
        "foo": { "type": "string" }
      },
      "required": ["key_name", "foo"],
      "additionalProperties": false
    },
    {
      "type": "object",
      "properties": {
        "key_name": { "const": "value2" },
        "bar": { "type": "string" }
      },
      "required": ["key_name", "bar"],
      "additionalProperties": false
    }
  ]
}
Carsten
  • 2,047
  • 1
  • 21
  • 46
  • Thanks for the great suggestion. However, the 1st solution works on "blacklisting" the keys which we don't want. Is there any way which works on "whitelisting" the keys, which we want, i.e if I can mention that only "foo" is required when the "key_name" is "value1"? – Himani Garg Apr 27 '20 at 05:42
  • @HimaniGarg I've edited the answer to include an alternative "whitelisting" approach. Depending on other properties being present in that object, it may not be feasible though. In this simple example, it works fine. – Carsten Apr 27 '20 at 06:17
  • Thanks @Carsten. It is working. But if I use this, I get the following error: is not valid under any of the given schemas. While this solves the purpose, this error is not exactly explainable on why the JSON is not valid. I am expecting some error like: "Additional properties are not allowed ('bar' was unexpected)" (This is the default error whenever any additional key is present) – Himani Garg Apr 27 '20 at 06:55
  • If you want a blacklist error message, you’d have to go for the first or second (blacklist) approach I’m afraid. – Carsten Apr 27 '20 at 06:58
  • Is there something like "ONLY these keys are required, and if anything except these keys are provided", then the error will be thrown? – Himani Garg Apr 27 '20 at 07:07
  • The `maxProperties` should do pretty much that I’d have thought. – Carsten Apr 27 '20 at 07:10
  • No. The `maxProperties` does not tell you which keys are extra. It will just tell you that the JSON is not valid and the keys provided exceed the maximum count. – Himani Garg Apr 27 '20 at 08:26
  • Might be worth a separate question then. – Carsten Apr 27 '20 at 08:29
  • In the `oneOf` example, all the duplicated parts can be pulled out of the `oneOf`. All that needs to be there is `"oneOf": [{ "required": ["foo"] }, { "required": ["bar"] }]` which makes it much easier to maintain. – Jason Desrosiers Apr 28 '20 at 14:33
  • The intention was to somehow indicate that the other attributes are not desired without explicitly listing them as `not: required: [...]` – Carsten Apr 28 '20 at 14:51