Warm tip: This article is reproduced from stackoverflow.com, please click
json jsonschema

JSON Schema cross key constraint support using conditonal keywords

发布于 2020-04-07 10:08:21

I want to express conditional validation of schema that contains cross key conditions. Is there a support for this in JSON schema with the available conditional keywords ( allOf/anyOf/if/then/else)

JSON Schema :

{
    "type": "object",
    "additionalProperties": false,
    "properties": {
        "x": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "value": {
                    "type": "string"
                }
            }
        },
        "y": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "key1": {
                    "type": "string",
                    "enum": ["a", "b", "c", "d", "e"]
                },
                "key2": {
                    "type": "string",
                    "enum": ["x", "y", "m", "n", "r", "s"]
                }
            },
            "anyOf": [{
                    "allOf": [{
                            "if": {
                                "properties": {
                                    "key1": {
                                        "enum": ["a", "b"]
                                    }
                                }
                            },
                            "then": {
                                "properties": {
                                    "key2": {
                                        "enum": ["x", "y"]
                                    }
                                }
                            }
                        },
                        {
                            "if": {
                                "x": {
                                    "properties": {
                                        "value": {
                                            "const": "myVal"
                                        }
                                    }
                                }
                            },
                            "then": {
                                "properties": {
                                    "key2": {
                                        "enum": ["x", "y"]
                                    }
                                }
                            }
                        }
                    ]
                },
                {
                    "if": {
                        "properties": {
                            "key1": {
                                "enum": ["c", "d"]
                            }
                        }
                    },
                    "then": {
                        "properties": {
                            "key2": {
                                "type": "string",
                                "enum": ["m", "n"]
                            }
                        }
                    }
                }
            ]
        }
    }
}

Sample JSON instance would look like this

{
    "x": {
        "value": "myVal"
    },
    "y": {
        "key1": "a",
        "key2": "x"
    }
}

The condition that I want to express is the following 2 conditions

  1. If (x.value == "myVal" AND (y.key1 == "a" OR y.key1 == "b") then y.key2 should only have "x" or "y"
    (OR)

  2. If ( y.key1 == "c" OR y.key1 == "d") then y.key2 should contain only "m" or "n".

    (OR)

  3. y.key2 can take any of the allowed enum values defined in y.key2 property.

The conditional I have used the JSON Schema doesn't work. I tried validating using https://www.jsonschemavalidator.net/ .

Any help would be deeply appreciated :)

Thanks

Questioner
shyam0191
Viewed
234
gregsdennis 2020-01-31 19:18

So I think this is one of those cases where it's better to forget about the if/then/else keywords and just define the good states in a oneOf. (I suggest oneOf instead of anyOf because exactly one of these states should match.)

So for your schema, you want ONE OF

  1. ALL OF
    • x.value == "myVal"
    • y.key1 in ["a", "b"]
    • y.key2 in ["x", "y"]
  2. ALL OF
    • y.key1 in ["c", "d"]
    • y.key2 in ["m", "n"]
  3. ALL OF
    • NOT
      • ONE OF
        • ALL OF
          • x.value == "myVal"
          • y.key1 in ["a", "b"]
        • y.key1 in ["c", "d"]
    • true (or {} if you're pre-draft-6)

It looks like you have #1 and #2 worked out; just drop the conditional logic. It's the NOT in #3 that does the work. Here we're saying that if the condition for #1 is false and the condition for #2 is false, then any value already defined in the enum for y.key2 is fine.

The reason we have to explicitly express that we don't want the conditions for #1 and #2 is that without them, we just have a true schema, which allows everything (that's not previously constrained).

Now the other gotcha here is that you have x being used in one of your conditions, but your anyOf subschema is under the definition for y, so it can't see x at all. To fix this, you'll want to move that subschema to the root, as a sibling to properties. Here it can view the entire instance, not just the value in the y property.