JSON模式:“allof”和“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其他字段将是无效的。 我知道为此目的存在additionalProperties应该被设置为“false”。 但是当我设置"additionalProprties":false如下所示:

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

我得到一个validation错误( 在这里检查):

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

[v4validation规范的作者]

你已经偶然发现了JSON Schema中最常见的问题,也就是说,它根本无法像用户所期望的那样进行inheritance。 但同时也是它的核心特征之一。

当你这样做时:

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

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

在你的场景中,很多人遇到这种情况,你期望在schema1定义的属性对于schema1是已知的; 但事实并非如此,永远也不会。

这个问题就是为什么我为草案v5提出这两个提案:

  • strictProperties
  • merge

shipping_address模式将是:

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

以及在address strictProperties定义为true


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

现在,让我回到起草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" } ] } 

总而言之,这意味着在草案v3中的extends从来没有真正达到人们期望的效果。 随着草案v4, *Of关键字明确界定。

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

additionalProperties适用于所有不属于直接模式中的propertiespatternPropertiesproperties

这意味着当你有:

  { "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解决scheme的一个简化版本:

 { "$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模式中所需属性的validation,并在shipping_address添加所需的type属性。

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

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

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