JSON 模式: “ allof”和“ addtionalProperties”

假设我们有模式如下模式(来自教程 给你) :

{
"$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
}

我得到一个验证错误(选中 给你) :

[ {
"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的部分只? 预先感谢。

36668 次浏览

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将不适用于所需的属性。

[这里是 v4验证规范草案的作者]

你偶然发现了 JSON Schema 最常见的问题,即它根本无法像用户期望的那样进行继承,但同时这也是它的核心特性之一。

当你这样做:

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

schema1schema2具有相互的 没有知识; 它们在各自的上下文中进行评估。

在您的场景中(许多许多人都会遇到这种情况) ,您期望 schema1中定义的属性对于 schema2来说是已知的; 但是事实并非如此,也永远不会是这样。

这个问题就是我为什么就草案5提出这两项建议的原因:

那么,shipping_address的模式应该是:

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

以及在 address中定义 strictPropertiestrue


顺便说一下,我也是你提到的网站的作者。

现在,让我们回到草案 V3。草案 v3确实定义了 extends,它的值是一个模式或一组模式。根据这个关键字的定义,它意味着实例必须对 extends中指定的当前模式 还有有效; 基本上,草案 v4的 allOf就是草案 v3的 extends

考虑一下(第3版草案) :

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

现在:

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

他们是一样的,或许是这样?

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

还是那个?

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

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

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

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

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

由于没有人发布一个有效的答案规格 2019-09和以上,我几乎错过了安德烈亚斯 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"]
}
]
}
}
}

作者 给你给出了一个相当清晰简洁的解释;