【问题标题】:JSON schema : "allof" with "additionalProperties"JSON 模式:“allof”和“additionalProperties”
【发布时间】:2014-05-06 13:28:18
【问题描述】:

假设我们有模式跟随模式(来自教程here):

{
  "$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 的任何其他字段都将无效。我知道为此目的存在additionalProperties,它应该设置为“false”。但是当我将"additionalProprties":false 设置如下:

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

我收到一个验证错误(检查here):

[ {
  "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 部分的字段?提前致谢。

【问题讨论】:

    标签: javascript json jsonschema


    【解决方案1】:

    这是Yves-M's Solution 的略微简化版本:

    {
      "$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 模式中声明的类型约束(和任何其他约束)。

    【讨论】:

      【解决方案2】:

      不要在定义级别设置 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

      【讨论】:

      • 如果我没看错的话, billing_address 的定义是通过引用属性 street_address、city 和 state 引入的;然后它在本地再次定义它们。那正确吗? shipping_address 似乎做了类似的事情。它似乎是多余的,我使用 $ref 的目标是避免重复定义。请告诉我,这有什么作用?
      • 您必须在properties 中再次指定street_addresscitystate 才能在required 中使用它们,这就是它们在properties{} 中的原因
      • "您必须在属性中再次指定 street_address、city 和 state 才能在需要时使用它们,这就是它们在带有 {} 的属性中的原因"。什么不,你不。无论如何都不符合规范。必填,属性是独立的。
      • 这显然违反了DRY,这就是OP首先提出这个问题的原因
      • 我同意这会破坏 DRY。除此之外,它产生的代码可读性较差,而且绝不是直观的。我不确定是否有原因为什么 additionalProperties 在检查允许的属性时只查看同级级别,但恕我直言,这应该更改。
      【解决方案3】:

      [此处是 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 中定义strictPropertiestrue


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

      现在,让我回到草稿 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 关键字被明确定义。

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

      【讨论】:

      • 这两个属性不是任何规范的一部分——它们是@fge 提议的特性,所以不能保证它们会出现在任何未来的版本中。
      • @fge - 在您的回答中,关键字不是规范的一部分,而是您提议的扩展,这有点不明显。它们的呈现方式使它们看起来像是一个官方解决方案,而没有任何关于 v5 不断变化的警告,我认为这具有误导性。
      • 目前在 v7,我没有看到任何 mergestrictProperties。现在可以做OP所说的吗?
      • @JulianHonma 2019 年 1 月 strictPropertiesmerge 不属于规范草案 v7。
      • 他们现在的版本是2019-09,但在这方面仍然没有任何改进的迹象。
      【解决方案4】:

      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 将不适用于您想要的属性。

      【讨论】:

      • 感谢您的回复,但这无济于事:这是错误:[ {“level”:“error”,“schema”:{“loadingURI”:“#”,“pointer”:“ /properties/shipping_address" }, "instance" : { "pointer" : "/shipping_address" }, "domain" : "validation", "keyword" : "additionalProperties", "message" : "其他属性是不允许的", "unwanted" : [ "city", "state", "street_address" ] } ] - 和以前一样。
      • 您是否导入了“city”“state”和“street_address”,例如我的示例中的“addressProp1”?
      • 你能澄清一下吗?我根据您的更改更改了架构。 "city" "state" 和 "street_address" 仍然是引用模式的一部分,你是认真的吗?我检查了验证器json-schema-validator.herokuapp.com
      • 这是目前唯一可行的解​​决方案,尽管相当麻烦(当您必须添加十几个属性以保持"additionalProperties": false 开心时)
      猜你喜欢
      • 2022-06-11
      • 2020-02-03
      • 2017-06-02
      • 1970-01-01
      • 2019-05-15
      • 2016-12-08
      • 2022-01-14
      • 2019-08-31
      • 1970-01-01
      相关资源
      最近更新 更多