假设我们有架构遵循架构(来自教程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
部分的字段?提前谢谢。
答案 0 :(得分:71)
[v4验证规范草案的作者]
您偶然发现了JSON Schema中最常见的问题,即它根本无法按用户的意愿进行继承;但与此同时,它是其核心特征之一。
当你这样做时:
"allOf": [ { "schema1": "here" }, { "schema2": "here" } ]
schema1
和schema2
没有彼此的知识;他们在自己的背景下进行评估。
在许多人遇到的情景中,您希望schema1
中定义的属性为schema2
;但情况并非如此,永远不会。
这就是为什么我为草案v5制定了这两个提案的原因:
shipping_address
的架构将是:
{
"merge": {
"source": { "$ref": "#/definitions/address" },
"with": {
"properties": {
"type": { "enum": [ "residential", "business" ] }
}
}
}
}
以及strictProperties
中true
到address
的定义。
顺便说一下,我也是您所指的网站的作者。
现在,让我回到草案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
关键字已明确定义。
但到目前为止,你遇到的问题是最常遇到的问题。因此,我的建议将一劳永逸地解决这种误解的来源!
答案 1 :(得分:7)
additionalProperties
适用于立即架构中未由properties
或patternProperties
计算的所有属性。
这意味着当你有:
{
"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
将不适用于您想要的属性。
答案 2 :(得分:5)
以下是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
架构中所需属性的验证,只需在type
中添加所需的shipping_address
属性。
令人遗憾的是additionalProperties
只考虑了直接的兄弟级属性。也许这是有原因的。但这就是为什么我们需要重复继承的属性。
在这里,我们使用空对象语法以简化形式重复继承的属性。这意味着具有这些名称的属性无论它们包含什么类型的值都是有效的。但是我们可以依赖allOf
关键字来强制执行基础address
模式中声明的类型约束(以及任何其他约束)。
答案 3 :(得分:1)
一切都会好的:
{
"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_address
和shipping_address
都应指定自己需要的属性。
如果你想将他的属性与其他属性结合起来,你的定义不应该有"additionalProperties": false
。