I would like to know if I can define a JSON schema (draft 2019-09) that requires at least one of many properties possible for an object, and some times multiple of those properties. I already know of allOf, anyOf, oneOf, and dependantrequried but just can't figure out how to use them in the way I want.
I have an object with four keys:
amount
productId
invoiceId
jwt
I have the follow requirements:
amount
, productId
, and invoiceId
can not be in the object togetherinvoiceId
must have jwt
jwt
may be alone or with amount
or product
I have tried this schema, this works in most cases excpet when jwt
is alone:
{
"type": "object",
"properties": {
"amount": {"type": "number"},
"productId": {"type": "string"},
"invoiceId": {"type": "string"},
"jwt": {"type": "string"},
},
"dependantRequried": {
"invoiceId": ["jwt"]
},
"oneOf": [
{"required": ["amount"]},
{"required": ["productId"]},
{"required": ["invoiceId"]},
]
}
Adding {"required": ["jwt"]}
to "oneOf" array means that it fails when invoiceId
and jwt
are together.
expected results:
json | isValid |
---|---|
{"amount": 1} |
true |
{"amount": 1, "jwt": "jwt-test"} |
true |
{"productId": "prod-1"} |
true |
{"productId": "prod-1", "jwt": "jwt-test"} |
true |
{"invoiceId": "inv-1", "jwt": "jwt-test"} |
true |
{"jwt": "jwt-test"} |
true |
{"invoiceId": "inv-1"} |
false |
{"amount": 1, "productId": "prod-1"} |
false |
{"amount": 1, "productId": "prod-1", "jwt": "jwt-test"} |
false |
You have a couple options.
if
/then
/else
Instead of using required
directly, try an if
/then
construct to forbid the other properties when one is present.
{
// ...,
"anyOf": [
{
"if": {"required": ["amount"]},
"then": {
"properties": {
"productId": false,
"invoiceId": false,
}
},
"else": false
},
// repeat for productId and invoiceId
{
"properties": {
"amount": false,
"productId": false,
"invoiceId": false
}
}
]
}
The first subschema says that if amount
is present in the instance, the other two cannot have values, which is a roundabout way of saying they're disallowed. Repeat this for the other two properties.
The last one covers the case where none of them are present.
properties
This basically does the same thing, but some may consider it less readable. It's a style question, really.
{
// ...,
"anyOf": [
{
"properties": {
"amount": true,
"productId": false,
"invoiceId": false
}
},
// repeat for productId and invoiceId
{
"properties": {
"amount": false,
"productId": false,
"invoiceId": false
}
}
]
}
Here the first subschema allows only the amount
property. Again, repeat this for the other two properties, and have the same last case.
You also want to use anyOf
instead of oneOf
. oneOf
would work, but it's a more intensive check. Some implementations can shortcut and not evaluate the other subschemas once they've found one that works. For a oneOf
, though, all subschemas must always be checked. Since we've arranged this so that only one of these can be valid, anyOf
will do.
Lastly, you also have a typo: dependantRequried
-> dependentRequired
.
You can test these out at https://json-everything.net/json-schema.