JSON 模式:“allof”和“additionalProperties”
Posted
技术标签:
【中文标题】JSON 模式:“allof”和“additionalProperties”【英文标题】:JSON schema : "allof" with "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
部分的字段?提前致谢。
【问题讨论】:
【参考方案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_address
和shipping_address
都应指定自己所需的属性。
如果你想将他的属性与其他属性结合起来,你的定义不应该有"additionalProperties": false
。
【讨论】:
如果我没看错的话, billing_address 的定义是通过引用属性 street_address、city 和 state 引入的;然后它在本地再次定义它们。那正确吗? shipping_address 似乎做了类似的事情。它似乎是多余的,我使用 $ref 的目标是避免重复定义。请告诉我,这有什么作用? 您必须在properties
中再次指定street_address
、city
和state
才能在required
中使用它们,这就是它们在properties
和
中的原因
"您必须在属性中再次指定 street_address、city 和 state 才能在需要时使用它们,这就是它们在带有 的属性中的原因"。什么不,你不。无论如何都不符合规范。必填,属性是独立的。
这显然违反了DRY,这就是OP首先提出这个问题的原因
我同意这会破坏 DRY。除此之外,它产生的代码可读性较差,而且绝不是直观的。我不确定是否有原因为什么 additionalProperties 在检查允许的属性时只查看同级级别,但恕我直言,这应该更改。【参考方案3】:
[此处是 v4 验证规范草案的作者]
您偶然发现了 JSON Schema 中最常见的问题,即它根本无法按照用户的期望进行继承;但同时它也是它的核心功能之一。
当你这样做时:
"allOf": [ "schema1": "here" , "schema2": "here" ]
schema1
和schema2
彼此不了解;它们在自己的上下文中进行评估。
在很多人遇到的场景中,您希望schema1
中定义的属性将为schema2
所知;但事实并非如此,而且永远不会。
这个问题是我为草案 v5 提出这两个提案的原因:
strictProperties
,
merge
.
shipping_address
的架构将是:
"merge":
"source": "$ref": "#/definitions/address" ,
"with":
"properties":
"type": "enum": [ "residential", "business" ]
在address
中定义strictProperties
到true
。
顺便说一句,我也是你所指网站的作者。
现在,让我回到草稿 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,我没有看到任何merge
或 strictProperties
。现在可以做OP所说的吗?
@JulianHonma 2019 年 1 月 strictProperties
和 merge
不属于规范草案 v7。
他们现在的版本是2019-09,但在这方面仍然没有任何改进的迹象。【参考方案4】:
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
将不适用于您想要的属性。
【讨论】:
感谢您的回复,但这无济于事:这是错误:[ “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
开心时)以上是关于JSON 模式:“allof”和“additionalProperties”的主要内容,如果未能解决你的问题,请参考以下文章
源码分析 | CompletableFuture 组合处理 allOf 和 anyOf 太赞了!
CompletableFuture.allOf().orTimeout() 的意外行为
带有集合或列表的 Java 8 CompletableFuture.allOf(...) [重复]