繁体   English   中英

JSON 模式:“allof”和“additionalProperties”

[英]JSON schema : "allof" with "additionalProperties"

假设我们有遵循模式的模式(来自这里的教程):

{
  "$schema": "http://json-schema.org/draft-04/schema#",

  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city":           { "type": "string" },
        "state":          { "type": "string" }
      },
      "required": ["street_address", "city", "state"]
    }
  },

  "type": "object",

  "properties": {
    "billing_address": { "$ref": "#/definitions/address" },
    "shipping_address": {
      "allOf": [
        { "$ref": "#/definitions/address" },
        { "properties":
          { "type": { "enum": [ "residential", "business" ] } },
          "required": ["type"]
        }
      ]
    } 

  }
}

这是有效的实例:

{
      "shipping_address": {
        "street_address": "1600 Pennsylvania Avenue NW",
        "city": "Washington",
        "state": "DC",
        "type": "business"
      }
}

我需要确保shipping_address的任何其他字段都无效。 我知道为此目的存在应设置为“false”的additionalProperties属性。 但是当我设置"additionalProprties":false时,如下所示:

"shipping_address": {
          "allOf": [
            { "$ref": "#/definitions/address" },
            { "properties":
              { "type": { "enum": [ "residential", "business" ] } },
              "required": ["type"]
            }
          ],
          "additionalProperties":false
        } 

我收到一个验证错误(在此处检查):

[ {
  "level" : "error",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/shipping_address"
  },
  "instance" : {
    "pointer" : "/shipping_address"
  },
  "domain" : "validation",
  "keyword" : "additionalProperties",
  "message" : "additional properties are not allowed",
  "unwanted" : [ "city", "state", "street_address", "type" ]
} ] 

问题是:我应该如何仅限制shipping_address部分的字段? 提前致谢。

[此处是 v4 验证规范草案的作者]

您偶然发现了 JSON Schema 中最常见的问题,即它根本无法按照用户期望的方式进行继承; 但同时它也是它的核心功能之一。

当你这样做时:

"allOf": [ { "schema1": "here" }, { "schema2": "here" } ]

schema1schema2彼此知道; 他们在自己的背景下进行评估。

在许多人遇到的场景中,您希望schema1中定义的属性将为schema2所知; 但事实并非如此,而且永远不会如此。

这个问题就是为什么我在 v5 草案中提出了这两个建议:

您的shipping_address架构将是:

{
    "merge": {
        "source": { "$ref": "#/definitions/address" },
        "with": {
            "properties": {
                "type": { "enum": [ "residential", "business" ] }
            }
        }
    }
}

以及在address strictProperties定义为true


顺便说一句,我也是您所指网站的作者。

现在,让我回到草稿 v3。 Draft v3 确实定义了extends ,它的值要么是一个模式,要么是一个模式数组。 通过这个关键字的定义,这意味着实例必须对当前模式extends中指定的所有模式有效; 基本上,草案 v4 的allOf是草案 v3 的extends

考虑一下(草案 v3):

{
    "extends": { "type": "null" },
    "type": "string"
}

现在,那:

{
    "allOf": [ { "type": "string" }, { "type": "null" } ]
}

他们是一样的。 或者也许是这样?

{
    "anyOf": [ { "type": "string" }, { "type": "null" } ]
}

或者那个?

{
    "oneOf": [ { "type": "string" }, { "type": "null" } ]
}

总而言之,这意味着草案 v3 中的extends从未真正做到人们期望它做的事情。 在草案 v4 中, *Of关键字已明确定义。

但是到目前为止,您遇到的问题是最常遇到的问题。 因此,我的建议将一劳永逸地消除这种误解的根源!

additionalProperties适用于直接模式中的propertiespatternProperties属性未考虑的所有属性。

这意味着当您拥有:

    {
      "allOf": [
        { "$ref": "#/definitions/address" },
        { "properties":
          { "type": { "enum": [ "residential", "business" ] } },
          "required": ["type"]
        }
      ],
      "additionalProperties":false
    }

这里的additionalProperties适用于所有属性,因为没有兄弟级别的properties条目- allOf里面的那个不算。

您可以做的一件事是将properties定义向上移动一级,并为您正在导入的属性提供存根条目:

    {
      "allOf": [{"$ref": "#/definitions/address"}],
      "properties": {
        "type": {"enum": ["residential", "business"]},
        "addressProp1": {},
        "addressProp2": {},
        ...
      },
      "required": ["type"],
      "additionalProperties":false
    }

这意味着additionalProperties将不适用于您想要的属性。

这是Yves-M 解决方案的略微简化版本:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": [
        "street_address",
        "city",
        "state"
      ]
    }
  },
  "type": "object",
  "properties": {
    "billing_address": {
      "$ref": "#/definitions/address"
    },
    "shipping_address": {
      "allOf": [
        {
          "$ref": "#/definitions/address"
        }
      ],
      "properties": {
        "type": {
          "enum": [
            "residential",
            "business"
          ]
        },
        "street_address": {},
        "city": {},
        "state": {}
      },
      "required": [
        "type"
      ],
      "additionalProperties": false
    }
  }
}

这保留了基address模式中所需属性的验证,并仅在shipping_address中添加所需的type属性。

不幸的是, additionalProperties只考虑了直接的兄弟级属性。 也许这是有原因的。 但这就是为什么我们需要重复继承的属性。

在这里,我们使用空对象语法以简化形式重复继承的属性。 这意味着具有这些名称的属性无论包含何种值都是有效的。 但是我们可以依靠allOf关键字来强制执行基address模式中声明的类型约束(以及任何其他约束)。

不要在定义级别设置 additionalProperties=false

一切都会好起来的:

{    
    "definitions": {
        "address": {
            "type": "object",
            "properties": {
                "street_address": { "type": "string" },
                "city":           { "type": "string" },
                "state":          { "type": "string" }
            }
        }
    },

    "type": "object",
    "properties": {

        "billing_address": {
            "allOf": [
                { "$ref": "#/definitions/address" }
            ],
            "properties": {
                "street_address": {},
                "city": {},
                "state": {}                 
            },          
            "additionalProperties": false
            "required": ["street_address", "city", "state"] 
        },

        "shipping_address": {
            "allOf": [
                { "$ref": "#/definitions/address" },
                {
                    "properties": {
                        "type": {
                            "enum": ["residential","business"]
                        }
                    }
                }
            ],
            "properties": {
                "street_address": {},
                "city": {},
                "state": {},
                "type": {}                          
            },              
            "additionalProperties": false
            "required": ["street_address","city","state","type"] 
        }

    }
}

您的每个billing_addressshipping_address都应指定自己的必需属性。

你的定义不应该有"additionalProperties": false如果你想将他的属性与其他属性结合起来。

由于没有人为规范2019-09及更高版本发布有效答案,我几乎错过了 Andreas H. 的评论;

{
  "$schema": "http://json-schema.org/draft-04/schema#",

  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city":           { "type": "string" },
        "state":          { "type": "string" }
      },
      "required": ["street_address", "city", "state"]
      // additionalProperties: false    // <-- Remove completely if present 
    }
  },

  "type": "object",

  "properties": {
    "billing_address": { "$ref": "#/definitions/address" },
    "shipping_address": {
      "unevaluatedProperties": false,   // <-- Add to same level as allOf as false
      "allOf": [
        { "$ref": "#/definitions/address" },
        { "properties":
          { "type": { "enum": [ "residential", "business" ] } },
          "required": ["type"]
        }
      ]
    } 
  }
}

作者在这里可以找到一个非常清晰简洁的解释;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM