最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

jsonschema - JSON Schema validate items in an array with conditional if, then - Stack Overflow

programmeradmin5浏览0评论

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 badges
Add a comment  | 

2 Answers 2

Reset to default 1

Your 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/thens,

      "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.

发布评论

评论列表(0)

  1. 暂无评论