We are using JSON Schema to validate an array of items but the validation does not seem to report in a sensible manner. An example schema is:
{
"$schema": ";,
"type": "object",
"properties": {
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item": { "type": "string", },
"value": { "type": "string" }
},
"required": ["item", "value"],
"additionalProperties": false
},
"allOf": [
{
"if": {
"contains": {
"required": ["item", "value"],
"additionalProperties": false,
"properties": {
"item": { "const": "include" },
"value": { "const": "yes" }
}
}
},
"then": {
"allOf": [
{
"contains": {
"type": "object",
"properties": {
"item": { "const": "1" },
"value": { "const": "1" }
}
}
},
{
"contains": {
"type": "object",
"properties": {
"item": { "const": "2" },
"value": { "enum": ["A", "B", "C"] }
}
}
}
]
}
}
]
}
}
}
Example JSON to validate is the following:
{
"options": [
{ "item": "include", "value": "yes" },
{ "item": "1", "value": "1" },
{ "item": "2", "value": "Z" }
]
}
So item 2 having a value of Z is not valid as it can only be A, B or C. I would have expected the error to report that item 2's value is invalid but instead the validation seems to report 9 errors with only one being related to the actual validation that has failed.
How do we validate the JSON but report errors in a sensible form that identifies what the actual error is?
We are using JSON Schema to validate an array of items but the validation does not seem to report in a sensible manner. An example schema is:
{
"$schema": "https://json-schema./draft/2020-12/schema",
"type": "object",
"properties": {
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item": { "type": "string", },
"value": { "type": "string" }
},
"required": ["item", "value"],
"additionalProperties": false
},
"allOf": [
{
"if": {
"contains": {
"required": ["item", "value"],
"additionalProperties": false,
"properties": {
"item": { "const": "include" },
"value": { "const": "yes" }
}
}
},
"then": {
"allOf": [
{
"contains": {
"type": "object",
"properties": {
"item": { "const": "1" },
"value": { "const": "1" }
}
}
},
{
"contains": {
"type": "object",
"properties": {
"item": { "const": "2" },
"value": { "enum": ["A", "B", "C"] }
}
}
}
]
}
}
]
}
}
}
Example JSON to validate is the following:
{
"options": [
{ "item": "include", "value": "yes" },
{ "item": "1", "value": "1" },
{ "item": "2", "value": "Z" }
]
}
So item 2 having a value of Z is not valid as it can only be A, B or C. I would have expected the error to report that item 2's value is invalid but instead the validation seems to report 9 errors with only one being related to the actual validation that has failed.
How do we validate the JSON but report errors in a sensible form that identifies what the actual error is?
Share Improve this question edited Mar 25 at 14:05 Jeremy Fiel 3,3652 gold badges12 silver badges26 bronze badges asked Mar 25 at 8:59 Andy BAndy B 455 bronze badges2 Answers
Reset to default 1Your schema was mostly correct, your conditional statement was at the wrong nested level and you missed adding the properties
keyword in your conditional statement.
The conditional should be at the same level as the schema you are trying to validate, or "higher" so you can drill into a schema. You can't drill out of a schema.
{
"$schema": "https://json-schema./draft/2020-12/schema",
"type": "object",
"properties": {
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item": {"type": "string"},
"value": {"type": "string"}
},
"required": ["item", "value"],
"additionalProperties": false
}
}
},
"allOf": [
{
"if": {
"properties": {
"options": {
"contains": {
"required": ["item", "value"],
"properties": {
"item": {"const": "include"},
"value": {"const": "yes"}
}
}
}
}
},
"then": {
"allOf": [
{
"properties": {
"options": {
"contains": {
"type": "object",
"properties": {
"item": {"const": "1"},
"value": {"const": "1"}
}
}
}
}
},
{
"properties": {
"options": {
"contains": {
"type": "object",
"properties": {
"item": {"const": "2"},
"value": {"enum": ["A","B","C"]
}
}
}
}
}
}
]
}
}
]
}
The reason you get so many errors is because contains
checks every item and will report all the failures if there's no match. You end up with at least one error message for each item in the item in the array times the number of contains
loops you have.
You can improve the situation with more if
/then
s,
"allOf": [
{
"if": {
"contains": {
"required": ["item", "value"],
"properties": {
"item": { "const": "include" },
"value": { "const": "yes" }
}
}
},
"then": {
"allOf": [
{
"items": {
"if": {
"properties": {
"item": { "const": "1" }
}
},
"then": {
"properties": {
"value": { "const": "1" }
}
}
},
"contains": {
"properties": {
"item": { "const": "1" }
}
}
},
{
"items": {
"if": {
"properties": {
"item": { "const": "2" }
}
},
"then": {
"properties": {
"value": { "enum": [ "A", "B", "C" ] }
}
}
},
"contains": {
"properties": {
"item": { "const": "2" }
}
}
}
]
}
}
]
This breaks it down to,
- If "item" is "1", then "value" must be "1"
- "options" must contain an object where "item" is "1"
- If "item" is "2", then "value" must be one of "A", "B", or "C"
- "options" must contain an object where "item" is "2"
At least with the given example, the contains
keywords would pass and you would avoid verbose messaging you get from that keyword. You should only get the one message you expect about the value of item 2.
However, this does something slightly different than the original schema. The original would allow additional item 2 entries with different values. For example,
{
"options": [
{ "item": "include", "value": "yes" },
{ "item": "1", "value": "1" },
{ "item": "2", "value": "Z" },
{ "item": "2", "value": "A" }
]
}
As long as there was one item 2 that matches, the schema passes. The version I gave would say that the item 2 with value "Z" would fail. If you can be even more strict than that, you can use prefixItems
.
"if": {
"prefixItems": [
{
"const": { "item": "include", "value": "yes" }
}
}
},
"then": {
"prefixItems": [
true,
{
"const": { "item": "1", "value": "1" }
},
{
"properties": {
"item": { "const": "2" },
"value": { "enum": [ "A", "B", "C" ] }
}
}
],
"items": false,
"minItems": 3
}
This does the same thing, except order is relevant. Hopefully that works for you because it's a much nicer schema and will evaluate much more efficiently.