How to conditionally forbid properties based on presence of other properties in JSON Schema?
Dependencies
This is a job for the dependencies
keyword. The following says
- if "locale" is present, then "index_name" is forbidden.
- if "environment" is present, then "index_name" is forbidden.
|
"dependencies": {
"locale": { "not": { "required": ["index_name"] } },
"environment": { "not": { "required": ["index_name"] } }
}
What's up with not
-required
?
There's a sub question about how not
-required
works. It's confusing because it doesn't mean how it reads in English, but it's similar enough to make us think it does sometimes.
In the above example, if we read it as "not required", it sounds like it means "optional". A more accurate description would be "forbidden".
That's awkward, but not too bad. Where it gets confusing is when you want to "forbid" more than one property. Let's assume we want to say, if "foo" is present, then "bar" and "baz" are forbidden. The first thing you might try is this.
"dependencies": {
"foo": { "not": { "required": ["bar", "baz"] } }
}
However, what this says is that if "foo" is present, then the instance is invalid if both "bar" AND "baz" are present. They both have to be there to trigger failure. What we really wanted is for it to be invalid if "bar" OR "baz" are present.
"dependencies": {
"foo": {
"not": {
"anyOf": [
{ "required": ["bar"] },
{ "required": ["baz"] }
]
}
}
}
Why is this so hard?
JSON Schema is optimized for schemas that are tolerant to changes. The schema should enforce that the instance has a the necessary data to accomplish a certain task. If it has more than it needs, the application ignores the rest. That way, if something is add to the instance, everything still works. It shouldn't fail validation if the instance has a few extra fields that the application doesn't use.
So, when you try to do something like forbidding things that you could otherwise ignore, you're going a bit against the grain of JSON Schema and things can get a little ugly. However, sometimes it's necessary. I don't know enough about your situation to make that call, but I'd guess that dependencies
is probably necessary in this case, but additionalProperties
is not.