Description
The pattern of using oneOf
to describe a choice between two types has become ubiquitous.
{
"oneOf": [
{ "$ref": "#/$defs/aaa" },
{ "$ref": "#/$defs/bbb" }
]
}
However, this pattern is also notorious for its many shortcomings. There is a better way to describe this kind of constraint that doesn't have all the problems of the oneOf
pattern, but it's verbose, error prone, and not particularly intuitive.
{
"allOf": [
{
"if": {
"properties": {
"foo": { "const": "aaa" }
},
"required": ["foo"]
},
"then": { "$ref": "#/$defs/foo-aaa" }
},
{
"if": {
"properties": {
"foo": { "const": "bbb" }
},
"required": ["foo"]
},
"then": { "$ref": "#/$defs/foo-bbb" }
}
]
}
OpenAPI addresses this problem with the discriminator
keyword. However their approach is more oriented toward code generation concerns and is poorly specified when it comes to validation. I don't think we should adopt discriminator
, but I do think we need something like it. I believe this is the thing that is generating the most questions in our community right now.
Right now, we have the dependentSchemas
keyword that is very close to what is needed except it checks for the presence of a property rather than it's value. The propertyDependencies
keyword builds on that concept to solve the problem.
{
"propertyDependencies": {
"foo": {
"aaa": { "$ref": "#/$defs/foo-aaa" },
"bbb": { "$ref": "#/$defs/foo-bbb" }
}
}
}
If the instance is an object, then for every property (name) and value of that property (value), if /propertyDependencies/{name}/{value}
is defined, then the instance must be valid against the schema at that location. Compared to discriminator
, this is more consistent with the style of JSON Schema keywords because it doesn't use sub-keywords like propertyName
and mappings
. It's also more powerful because it allows you to discriminate using more than one property.
Because of the parallels to dependencies
, I chose to name it in similar way. However, dependencies
is the least well known and understood keyword, so it might not be beneficial to build on that naming convention. Either way, I don't think we should call it discriminator
to avoid confusion with what OpenAPI has specified.