【问题标题】:Extra error message by JSON Schema validator when if-else is there当 if-else 存在时 JSON Schema 验证器的额外错误消息
【发布时间】:2020-04-16 18:02:21
【问题描述】:

这是我的 JSON 架构(假设的,因为我无法分享我的实际架构)和 JSON。 if-then-else 条件如下:

一次只能出现三 (3) 个国家中的一 (1) 个来定义统计数据。

架构

    {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "required": [
            "year",
            "country",
            "statistics"
        ],
        "definitions": {
        "statisticsUsa": {
          "if": {
            "properties": {
              "type": {
                "const": "statisticsUsa"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "type": {
                "const": "statisticsUsa"
              }
            }
          }
        },
            "statisticsFinland": {
          "if": {
            "properties": {
              "type": {
                "const": "statisticsFinland"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "type": {
                "const": "statisticsFinland"
              }
            }
          }
        },
        "statisticsGermany": {
          "if": {
            "properties": {
              "type": {
                "const": "statisticsGermany"
              }
            },
            "required": [
              "type", "literacy"
            ]
          },
          "then": {
            "properties": {
              "type": {
                "const": "statisticsGermany"
              },
              "literacy": {
                "type": "string"
              }
            }
          }
        }
        },
        "properties": {
            "year": {
                "type": "string"
            },
            "country": {
                "type": "string",
                "enum": [
                    "USA",
                    "FINLAND",
                    "GERMANY"
                ]
            },
            "statistics": {

                "type": "array",
                "allOf": [{
                        "if": {
                            "contains": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "statisticsUsa"
                                        ]
                                    }
                                }
                            }
                        },
                        "then": {
                            "not": {
                                "contains": {
                                    "type": "object",
                                    "properties": {
                                        "type": {
                                            "type": "string",
                                            "enum": [
                                                "statisticsFinland",
                                                "statisticsGermany"
                                            ]
                                        }
                                    }
                                }
                            }
                        },"errorMessage" :"Only USA applicable at a time for statistics"
                    },
                    {
                        "if": {
                            "contains": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "statisticsFinland"
                                        ]
                                    }
                                }
                            }
                        },
                        "then": {
                            "not": {
                                "contains": {
                                    "type": "object",
                                    "properties": {
                                        "type": {
                                            "type": "string",
                                            "enum": [
                                                "statisticsUsa",
                                                "statisticsGermany"
                                            ]
                                        }
                                    }
                                }
                            }
                        },"errorMessage" :"Only Finland applicable at a time for statistics"
                    },
                    {
                        "if": {
                            "contains": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "statisticsGermany"
                                        ]
                                    }
                                }
                            }
                        },
                        "then": {
                            "not": {
                                "contains": {
                                    "type": "object",
                                    "properties": {
                                        "type": {
                                            "type": "string",
                                            "enum": [
                                                "statisticsUsa",
                                                "statisticsFinland"
                                            ]
                                        }
                                    }
                                }
                            }
                        },"errorMessage" :"Only Germany applicable at a time for statistics"
                    }

                ],
                "minItems": 1,
                "items": {
                    "anyOf": [{
                            "$ref": "#/definitions/statisticsUsa"
                        },
                        {
                            "$ref": "#/definitions/statisticsFinland"
                        },
                        {
                            "$ref": "#/definitions/statisticsGermany"
                        }
                    ]
                }
            }
        }
    }

Json 1

    const Ajv = require('ajv');
    const ajv = new Ajv({
        allErrors: true,
        jsonPointers: true
    });

    const schema1 = require("./sof.json");
    require('ajv-errors')(ajv /*, {singleError: true} */);
    const data = {
      year: "2000",
      country: "USA",
      statistics: [
        {"type":"statisticsUsa"},
        {"type":"statisticsFinland"}
      ]
    }

    let valid = ajv.validate(schema1, data); //schema, data
    if (!valid) {
      console.log(`${JSON.stringify(ajv.errors,null,2)}`);
    }

在这里,当我有 2 个国家/地区时,我可以看到 2 个错误作为数组的一部分。我的问题是,“芬兰”不能与“美国”一起使用,“仅美国一次适用于统计数据”的错误是不可接受的,因为它满足条件。但是,Findland 的错误是可以接受的。

    [
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/0/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only USA applicable at a time for statistics"
      },
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/1/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only Finland applicable at a time for statistics"
      }
    ]

另外,如果我在 JSON 中添加“德国”,

{
  year: "2000",
  country: "USA",
  statistics: [
    {"type":"statisticsUsa"},
    {"type":"statisticsFinland"},
    {"type":"statisticsGermany"}
  ]
}

它会抛出 3 个错误作为数组的一部分(见下文)。为什么?

  1. 从技术上讲,架构验证器应停止在第二项,并应忽略错误数组中的“德国”,因为它发现“Findland”条目违反了 if-else 条件。
  2. 另外,schema-validator 是否从“statistics”数组(来自 JSON)中获取每个项目,并为每次运行检查“allOf”中的条件,这就是为什么数组中有 3 个错误条目。我的理解是否正确。 (如果您看到上面的第一个错误数组,它只有 2 个条目)

    [
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/0/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only USA applicable at a time for statistics"
      },
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/1/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only Finland applicable at a time for statistics"
      },
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/2/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/2/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/2/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only Germany applicable at a time for statistics"
      }
    ]

【问题讨论】:

    标签: jsonschema json-schema-validator ajv


    【解决方案1】:

    您自己已经推断出来了:allOf contains 应用所有子模式并检查所有数组项。

    如果您只想要一条错误消息,那么您应该确定国家/地区的优先级,并且只检查其余国家/地区的后续allOf 部分,而不是每次都检查所有国家/地区。例如

    1. 如果数组包含“USA”条目,则不应出现“GERMANY”或“FINLAND”(因为您已经拥有它)
    2. 如果数组包含“GERMANY”条目,则不应存在“FINLAND”条目(不要再次检查“USA”)
    3. 如果数组包含“FINLAND”条目,则上述两种检查已经涵盖了可能的场景——此处无需任何额外检查。

    这样,您永远不会同时遇到多个此类错误。

    【讨论】:

    • 感谢您的回复。但在更广泛的情况下,如果我在数组中有一个大条目并且针对 allOf 进行检查,那么很有可能会有这么多的错误消息,这是不可避免的。另外,以更简洁的方式将此类错误消息传递回调用者的最佳方法是什么(尽管我使用的是 ajv-errors npm)?
    • 不确定如何改进验证错误的呈现方式。但是,您也可以接受返回的多个错误消息,并将它们改写为“发现多个国家/地区,但只允许一个国家/地区”。 found: USA” 并且为每种类型返回相同的错误(以您现在拥有的相同方式)。
    • 另一种选择是使用oneOf 而不是allOf。您仍然会收到多条错误消息,但它们会明确指出 oneOf 中有多个有效条目。每个条目都可以定义为“array contains(X) and not(contains(anything but X))”——如果允许的话,还有一个额外的“array is empty” case。
    • 谢谢@Carsten。将尝试 oneOf 选项
    猜你喜欢
    • 2022-08-16
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2019-07-02
    • 1970-01-01
    • 2018-12-11
    • 2020-04-16
    相关资源
    最近更新 更多