Suppose I have an object with four possible properties: a, b, c, d. a and b can only appear together (i.e., a appears if and only if b appears). If a and b appear, c cannot appear (that is, a/b and c are mutually exclusive). If a and b do not appear, c may appear (but is not required to). d can appear in any combination with a/b, c, or on its own. No properties other than a, b, c, or d may appear at all.
How do I express this as a jsonschema? I suspect I could use some combination of oneOf
and required
, but I can't figure out the proper incantation.
You can phrase your constraints as:
- either: both
"a"
and "b"
are present, and "c"
is not present
- or: neither
"a"
nor "b"
is present. ("c"
may or may not be present)
Saying "neither" in the second point is a bit verbose. Here, we've expressed it using allOf
/not
. (Note: you can't factor them into a single required
clause here, because you need a separate not
for each one.)
{
"oneOf": [
{
"required": ["a", "b"],
"not": {"required": ["c"]}
},
{
"allOf": [
{
"not": {"required": ["a"]}
},
{
"not": {"required": ["b"]}
}
]
}
]
}
Alternative structure
There's also another way to say "neither", which is actually to use oneOf
again. Since you must pass exactly one of a oneOf
clause, if one of the entries is {}
(passes everything), then all the other options are banned.
While it's slightly more concise, it's possibly slightly less intuitive to read:
{
"oneOf": [
{
"required": ["a", "b"],
"not": {"required": ["c"]}
},
{
"oneOf": [
{},
{"required": ["a"]},
{"required": ["b"]}
]
}
]
}
Another alternative is to use the schema dependencies declaration:
"dependencies": {
"c": {
"allOf": [
{
"not": { "required": [ "a" ] }
},
{
"not": { "required": [ "b" ] }
}
]
},
"a": {
"allOf": [
{
"not": { "required": [ "c" ] }
},
{
"required": [ "b" ]
}
]
},
"b": {
"allOf": [
{
"not": { "required": [ "c" ] }
},
{
"required": [ "a" ]
}
]
}
}