JSON架构:带有"附加属性"的"全部"

bsxbgnwa  于 2023-02-10  发布在  其他
关注(0)|答案(5)|浏览(160)

假设我们有以下模式(从教程这里):

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

yqkkidmi

yqkkidmi1#

[此处为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,草案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" } ]
}

总而言之,这意味着draft v3中的extends从来没有真正做到人们期望它做的事情,而有了draft v4,*Of关键字得到了明确的定义。
但你遇到的问题是迄今为止最常见的问题,因此我的建议将一劳永逸地消除这一误解的根源!

waxmsbnn

waxmsbnn2#

additionalProperties适用于 * 直接模式 * 中propertiespatternProperties未说明的所有属性。
这意味着,当您具有:

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

additionalProperties在这里适用于 all 属性,因为没有兄弟级别的properties条目-allOf中的条目不计算在内。
您可以做的一件事是将properties定义上移一级,并为要导入的属性提供存根条目:

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

这意味着additionalProperties将不适用于所需的属性。

hc8w905p

hc8w905p3#

下面是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模式中声明的类型约束(以及任何其他约束)。

bfrts1fy

bfrts1fy4#

不要在定义级别设置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的属性与其他属性合并,则您的定义不应包含"additionalProperties": false

z3yyvxxp

z3yyvxxp5#

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

{
  "$schema": "http://json-schema.org/2019-09/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"]
        }
      ]
    } 
  }
}

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

相关问题