I have a JSON schema like this:
{
"authentication": {
// provider is an enum. Let's say it has A and B as values.
// if A is chosen, jwt must have a value.
// if B is chosen, jwt must NOT have a value
// Let's say JWT has 3 requirede properties. audience, issuer, and baz
"provider": "B",
"jwt": { "audience": "foo", "issuer": "bar" }
}
}
This is my current validation:
"allOf": [
{
"$comment": "We want the user to provide the JWT property when the provider requires it",
"if": {
"properties": {
"provider": {
"anyOf": [
{ "const": "A" },
]
}
},
"required": ["provider"]
},
"then": {
"required": ["jwt"]
}
},
{
"$comment": "We want JWT property to be null when a provider does not require it. This reduces bugs and confusion.",
"if": {
"properties": {
"provider": {
"not": {
"anyOf": [
{ "const": "A" }
]
}
}
},
"required": ["provider"]
},
"then": {
"properties": {
"jwt": {
"type": "null"
}
}
}
}
]
This works, but it isn't a great experience:
jwt
: Missing property "baz".
audience
and issuer
: Incorrect type. Expected "null".
The only error that would make sense would be that jwt
should not have a value because of the chosen provider. This is represented by Incorrect type. Expected "null".
, but it isn't obvious enough.
So, my questions:
if/else
?baz
property", which is irrelevant anyway as the parent property (jwt
) shouldn't exist anyway?This doesn't provide custom error output, but the output is much cleaner
Basically, you only check for the fail condition, if B
then disallow jwt
. Otherwise, require jwt
and the root schema provides the constraints for jwt
.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": ["authorization"],
"properties": {
"authorization": {
"required": ["provider"],
"properties": {
"provider": {"type": "string"},
"jwt": {
"required": ["audience", "issuer", "baz"],
"properties": {
"audience": {},
"issuer": {},
"baz": {}
}
}
},
"allOf": [
{
"if": {
"properties": {
"provider": {"const": "B"}
}
},
"then": {
"properties": {"jwt": false}
},
"else": {
"required": ["jwt"]
}
}
]
}
}
}
the only difference between null and disallowed would be changing the then
statement
"then": {
"properties": {
"jwt": {
"const": null
}
}
}
authorization:
provider: A
jwt:
audience: true
issuer: true
baz: true
authorization:
provider: B
authorization:
provider: B
jwt: null