Json 模式验证:对子模式的依赖

Json Schema Validation: Dependencies on subschema

我是 json 模式验证的新手,我无法根据 json

中更深层字段的存在和值来验证必填字段

下面是我当前的架构和示例 json。 我需要让它说如果 "SW Large" 存在并且值为 true 那么它要求 "SW Words" higher 存在于 Register 对象

下面的架构中的 "anyOf" 是我的尝试。

如果 "SW Large" 为真,则需要 "SW Words" 否则不需要,但如果字段 "SW Large" 不存在,它仍然会坚持认为 "SW Words" 是必填。

如何更改 "anyOf" 和 "allOf" 以检查 "SW Large" 是否确实存在?

编辑:剥离 json 以便更易于管理

架构:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "Registers"
  ],
  "properties": {
    "Registers": {
      "$id": "#/properties/Registers",
      "type": "array",
      "items": {
        "$id": "#/properties/Registers/items",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "Name",
          "Address",
          "Fields"
        ],
        "anyOf": [
          {
            "allOf": [
              { "not": { "properties": { "Fields" : { "items": { "properties": { "SW Large": { "const": true } } } } } } }
            ]
          },
          {
            "required": ["SW Words"]
          }
        ],
        "properties": {
          "Name": {
            "$id": "#/properties/Registers/items/properties/Name",
            "type": "string"
          },
          "Address": {
            "$id": "#/properties/Registers/items/properties/Address",
            "type": "string"
          },
          "SW Words": {
            "$id": "#/properties/Sections/items/properties/Blocks/items/properties/SW Words",
            "type": "integer",
            "default": 2,
            "minimum": 2
          },
          "Fields": {
            "$id": "#/properties/properties/Registers/items/properties/Fields",
            "type": "array",
            "items": {
              "$id": "#/properties/Registers/items/properties/Fields/items",
              "type": "object",
              "additionalProperties": false,
              "required": [
                "Name"
              ],
              "properties": {
                "Name": {
                  "$id": "#/properties/Registers/items/properties/Fields/items/properties/Name",
                  "type": "string"
                },
                "SW Large": {
                  "$id": "#/properties/Registers/items/properties/Fields/items/properties/SW Large",
                  "type": "boolean"
                }
              }
            }
          }
        }
      }
    }
  }
}

示例Json

{
  "Registers": [
    {
      "Name": "device",
      "Address": "100",
      "SW Words": 2,
      "Fields": [
        {
          "Name" : "Product",
          "SW Large" : true
        },
        {
          "Name": "Version"
        }
      ]
    }
  ]
}

Yuk,向上依赖真的很不愉快。模型必须这样塑造吗?

解决方案

你走在正确的轨道上。您缺少的是正确检查 "Fields" 数组中的至少一个元素是否具有 "SW Large" : true 然后塑造适当的依赖关系。

从 draft-06 开始用 "contains" 关键字解决。为了不重复内容,我建议阅读以下内容:

(很有教育意义!)

https://json-schema.org/understanding-json-schema/reference/array.html

您的架构在下面重新设计。 请参阅 "definitions" 部分

首先它添加 "contains" : { schema } 到 "Fields" 定义。我需要它作为单独的模式,以便将它用作逻辑含义中的条件。

"definitions" : {
    "Fields-contains-at-least-1-element-with-SW-Large-true" : {
      "properties": { 
        "Fields" : {
          "contains" : {
            "properties": { 
              "SW Large": { "enum": [true] } 
            },
            "required" : ["SW Large"]
          }
        } 
      },
    }
  },

如果您要永久添加它,它可能看起来像:

"Fields" : {
  "type" : "array",
  "contains" : {
    "properties": { 
      "SW Large": { "enum": [true] } 
    },
    "required" : ["SW Large"]
  }
  "items": {...},
}

翻译为“"Fields" 数组中的至少一项必须包含具有所述 属性 名称和所述值的对象”。每个 JSON with "Fields" 数组不包含 "SW Large" : true 将无法通过此类验证模式。如果您反转 "contains" 架构定义,例如:

"Fields" : {
    "type" : "array",
    "contains" : {
      "not" : {
        "properties": { 
          "SW Large": { "enum": [true] } 
        },
        "required" : ["SW Large"]
      }
    }
    "items": {...},
  }

它将转化为“"Fields" 数组中的任何项都不能包含具有所述 属性 名称和所述值的对象”。每个 JSON 与 "Fields" 数组包含至少一项 "SW Large" : true 将无法通过验证反对这种模式。

我不希望以上任何情况发生。我想 link "Fields/contains" 条件要求或不要求 "SW Words" 属性上一级,因此将模式排除在 "definitions" 部分并正确使用它。

"upward-dependency" 是使用 "anyOf" 关键字

的检查和正确的逻辑含义定义的

It doesn't contain at least 1 item with "SW Large" : true OR "SW Words" is required

"definitions" : {
    "upward-dependency" : {
      "anyOf" : [
        { "not" : {"$ref" : "#/definitions/Fields-contains-at-least-1-element-with-SW-Large-true"} },
        { "required" : ["SW Words"] }
      ]
    },
  },

在 "Registers" 数组的单项级别,我添加了 "dependencies"。每当 "Fields" 项目出现在 "Registers" 项目之一中时,都会检查它是否包含不幸的 "SW Large" : true 如果包含 - "SW Words" 成为必需的。瞧!

       "items" : {
        "$id": "#/properties/Registers/items
        "type" : "object",
        "properties" : {
          "Fields": {
            "$id": "#/properties/Registers/items/properties/Fields",
            "type": "array",
            "items": {
              "$id": "#/properties/Registers/items/properties/Fields/items",
              "type": "object",
              "propertyNames" : {
                "enum" : [
                  "Name",
                  "SW Large"
                ]                
              },
              "required": [
                "Name"
              ],
              "properties": {
                "Name": {
                  "$id": "#/properties/Registers/items/properties/Fields/items/properties/Name",
                  "type": "string"
                },
                "SW Large": {
                  "$id": "#/properties/Registers/items/properties/Fields/items/properties/SW Large",
                  "type": "boolean"
                }
              }
            }
          }
        },
        "dependencies" : {
          "Fields" : { "$ref" : "#/definitions/upward-dependency" }
        },
      }

我已经使用在线验证器检查了架构并将您的对象添加到 "examples" 部分。

完整架构:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "type": "object",
  "propertyNames" : {
    "enum" : [
      "Registers"
    ]
  },
  "required": [
    "Registers"
  ],
  "examples" : [
    {
      "Registers": [
        {
          "Name": "device",
          "Address": "100",
          "SW Words": 2,
          "Fields": [
            {
              "Name" : "Product",
              "SW Large" : true
            },
            {
              "Name": "Version"
            }
          ]
        }
      ]
    }
  ],
  "properties": {
    "Registers": {
      "$id": "#/properties/Registers",
      "type": "array",
      "items": {
        "$id": "#/properties/Registers/items",
        "type": "object",
        "propertyNames" : {
          "enum" : [
            "Name",
            "Address",
            "SW Words",
            "Fields"
          ]
        },
        "required": [
          "Name",
          "Address",
          "Fields"
        ],
        "properties": {
          "Name": {
            "$id": "#/properties/Registers/items/properties/Name",
            "type": "string"
          },
          "Address": {
            "$id": "#/properties/Registers/items/properties/Address",
            "type": "string"
          },
          "SW Words": {
            "$id": "#/properties/Sections/items/properties/Blocks/items/properties/SW Words",
            "type": "integer",
            "default": 2,
            "minimum": 2
          },
          "Fields": {
            "$id": "#/properties/Registers/items/properties/Fields",
            "type": "array",
            "items": {
              "$id": "#/properties/Registers/items/properties/Fields/items",
              "type": "object",
              "propertyNames" : {
                "enum" : [
                  "Name",
                  "SW Large"
                ]                
              },
              "required": [
                "Name"
              ],
              "properties": {
                "Name": {
                  "type": "string"
                },
                "SW Large": {
                  "type": "boolean"
                }
              }
            }
          }
        },
        "dependencies" : {
          "Fields" : { "$ref" : "#/definitions/upward-dependency" }
        },
      }
    }
  },
  "definitions" : {
    "upward-dependency" : {
      "anyOf" : [
        { "not" : {"$ref" : "#/definitions/Fields-contains-at-least-1-element-with-SW-Large-true"} },
        { "required" : ["SW Words"] }
      ]
    },
    "Fields-contains-at-least-1-element-with-SW-Large-true" : {
      "properties": { 
        "Fields" : {
          "contains" : {
            "properties": { 
              "SW Large": { "enum": [true] } 
            },
            "required" : ["SW Large"]
          }
        } 
      },
    }
  },
}

一些旁注:

请使用 "propertyNames" 而不是 "additionalProperties":false。专门引入它是为了确保只有需要的属性出现在 JSON 根据 ginst 模式验证的对象中。

"propertyNames" : {
    "enum" : [
      "Registers"
    ]
  }

参见:https://json-schema.org/understanding-json-schema/reference/object.html#property-names

根据应应用的级别正确调整 schemas/sub-schemas 非常重要。请注意 "#/definitions/Fields-contains-at-least-1-element-with-SW-Large-true" 如何正确反映 "#/Registers/items""#96=] 嵌套级别的架构(因为 "dependencies" 应用于 "#/Registers/items" 级别的对象,可以将其视为 ""#/Registers/items/依赖项”)。参见 https://json-schema.org/understanding-json-schema/reference/object.html#dependencies

如果您打算单独验证 "Fields" 数组的单个项目,甚至 "Registers" 的单个项目数组,您可能会考虑重塑您的架构以分离子架构,就像我在此处使用 "questionA" 所做的那样:。因此 - 如果有必要 - 您可以轻松地从主模式中排除子模式,并使用 "$ref" 关键字正确引用它们。一些关于 JSON 指针和相对 JSON 指针的阅读可能对构建复杂模式也很有用。

值得入手:https://json-schema.org/understanding-json-schema/structuring.html

希望对您有所帮助。