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

时间:2018-11-15 10:54:22

标签: json jsonschema

我是json模式验证的新手,我无法根据json中更深的字段的存在和值来验证必填字段

下面是我当前的模式和一个示例json。 我需要说如果“ SW Large”存在且值为真,那么它要求Register对象内存在更高的“ SW Words”

我在下面的架构中拥有的“ 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"
        }
      ]
    }
  ]
}

1 个答案:

答案 0 :(得分:0)

Y,向上的依赖关系确实令人不快。模型必须采用这种方式成形吗?

解决方案

您处在正确的轨道上。您缺少的是正确检查“字段”数组中的至少一个元素是否具有“ SW Large”:true ,然后确定适当的依赖关系。

自草案06起,使用“包含”关键字解决。为了不重复内容,建议阅读以下内容:

JSON Schema: How to check that an array contains at least one object with a property with a given value?

Json Schema: Require a property only when a specific property is present in a deep nested object(非常有教育意义!)

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

您的架构在下面进行了重新设计。 参见“定义”部分

首先,它在“字段” 定义中添加“ contains”:{ schema }。为了将其用作逻辑含义中的条件,我需要将其作为单独的架构。

"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,其中不包含带有“ SW Large”:true 的项目,将无法通过这种模式验证。如果您将“包含” 模式定义反转,例如:

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

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

我不希望以上任何事情发生。我想将“字段/包含” 条件与需要或不需要上一层“ SW Words” 属性的链接,因此将架构排除在“定义”部分之外,正确使用它。

“向上依赖” 是使用该检查和带有“ anyOf”关键字的适当逻辑含义定义的

  

它至少包含1个带有“ SW Large”(SW大)的项目:true或“ SW Words”为必填项

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

在“寄存器”数组的单个项目级别,我添加了“依赖项”。只要“字段”项出现在“注册”项之一中,就会检查它是否包含不幸的“ 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" }
        },
      }

我已经使用在线验证器检查了架构,并将您的对象添加到“示例”部分。

完整架构:

{
  "$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对象验证的aginst模式中仅存在所需的属性。

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

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

非常重要的一点是,应根据应在哪个级别上应用方案/子方案来正确调整方案/子方案的形状。请注意“#/ definitions / Fields-contains-at-least-1-element-with-SW-Large-true” 如何正确反映“#/ Registers / items” < / em>嵌套级别(因为“依赖项”应用于“#/ Registers / items” 级别的对象,因此可以将其视为“” ## / Registers / items / dependencies” )。请参见https://json-schema.org/understanding-json-schema/reference/object.html#dependencies

如果您打算分别验证“字段” 数组的单个项,或者甚至分别验证“ Registers” 数组的单个项,则可以考虑重塑架构以分隔单独的子项类似于我在这里https://stackoverflow.com/a/53309856/2811843中对“ questionA” 所做的处理。因此,如果有必要,您可以轻松地从主架构中排除子架构,并使用“ $ ref” 关键字正确引用它们。对JSON指针和相对JSON指针的一些阅读对于构造复杂模式也可能有用。

值得开始的地方:https://json-schema.org/understanding-json-schema/structuring.html

我希望它能帮上忙。