I have a legacy API I'm trying to define in a JSON Schema and the object has a weird structure where there are a set of 4 properties, any one of them is required, and 3 of them are mutually exclusive. The are a more than 30 shared optional properties after that as well, I'll note them as ...
.
e.g.,
{ "foo": "bar", "baz": 1234, ... } // OK
{ "foo": "bar", "buzz": 1234, ... } // OK
{ "foo": "bar", "fizz": 1234, ... } // OK
{ "foo": 1234, ... } // OK
{ "baz": 1234, ... } // OK
{ ... } // NOT OK
{ "baz": 1234, "buzz": 1234, ... } // NOT OK
I could do a oneOf
but that doesn't allow foo
to be present with the others, anyOf
allows for baz
,buzz
, and fizz
to be present with each other which is not possible.
I tried to define something like the following:
{
"type": "object",
"properties": {
"foo": {"type": "string"},
"baz": {"type": "number"},
"buzz": {"type": "number"},
"fizz": {"type": "number"}
},
"anyOf": [
{"required": ["foo"]},
{"required": [{"oneOf": [
{"required": ["baz"]},
{"required": ["buzz"]},
{"required": ["fizz"]}
]}
]}
]
}
and
{
"type": "object",
"properties": {
"foo": {"type": "string"},
"baz": {"type": "number"},
"buzz": {"type": "number"},
"fizz": {"type": "number"}
},
"anyOf": [
{"required": ["foo"]},
{"oneOf": [
{"required": ["baz"]},
{"required": ["buzz"]},
{"required": ["fizz"]}
]
}
]
}
But that does not work and I just don't know enough about json schema yet to know if this possible.
Interesting! There might be a neater solution, but here we go...
The "mutually exclusive" constraint can be expressed by banning pairwise combinations of the properties:
The "at least one of" constraint can be expressed with
anyOf
:If you just combine these two constraints into a single schema, then it should work:
You can make properties mutually exclusive in JSON Schema using pairwise exclusions, but this leads to combinatorial explosion. That becomes a problem when you have many mutually exclusive properties.
A linear solution is of the form:
This only pays off if you have many properties.
Combine this with @cloudfeet's answer to get the answer to your specific question.