我有一个json对象,如:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
sdk name
可以是android or ios
。 session_id
基于name field
的sdk json
。我使用条件语句(使用草案7)编写了一个json schema
,如下所示:
但它以一种意想不到的方式工作:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/Base",
"definitions": {
"Base": {
"type": "object",
"additionalProperties": false,
"properties": {
"session": {
"$ref": "#/definitions/Session"
},
"sdk": {
"$ref": "#/definitions/SDK"
}
},
"title": "Base"
},
"Session": {
"type": "object",
"additionalProperties": false,
"properties": {
"start_timestamp": {
"type": "integer",
"minimum": 0
},
"session_id": {
"type": "string",
"if": {
"SDK": {
"properties": {
"name": {
"enum": "ios"
}
}
}
},
"then": {
"pattern": "A"
},
"else": {
"pattern": "B"
}
}
},
"required": [
"session_id",
"start_timestamp"
],
"title": "Session"
},
"SDK": {
"type": "object",
"additionalProperties": false,
"properties": {
"version": {
"type": "string"
},
"name": {
"type": "string",
"enum": [
"ios",
"android"
]
}
},
"required": [
"name",
"version"
],
"title": "SDK"
}
}
}
所以下面的JSON通过:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "ios",
"version": "21"
}
}
但这失败了:
{
"session": {
"session_id": "B",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
有人能解释一下吗?..即便如此:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
我认为你遇到了与this question类似的问题。
@Relequestual是正确的,你需要围绕你的properties
标注的SDK
关键字。但是对于你想要做的事情,你需要重新组织。
子协议仅在实例中的级别上运行,而不是在根目录上运行。
对于包含one
和two
属性的简单JSON对象实例,请考虑此模式:
{
"properties": {
"one": {
"enum": ["yes", "no", "maybe"]
},
"two": {
"if": {
"properties": {
"one": {"const": "yes"}
}
},
"then": {
... // do some assertions on the two property here
},
"else": {
...
}
}
}
}
if
属性下的two
关键字只能考虑two
属性下的实例部分(即two
的值)。它不是在查看实例的根,所以根本看不到one
属性。
为了使two
属性子模式下的子模式可以在实例中看到one
属性,你必须将if
移到properties
关键字之外。
{
"if": {
"properties": {
"one": {"const" : "yes"}
}
},
"then": {
... // do some assertions on the two property here
},
"else": {
... // assert two here, or have another if/then/else structure to test the one property some more
}
}
对于两个可能的one
值,这是非常好的。即使三个可能的值也不错。但是,随着one
的可能值增加,if
s的嵌套也会增加,这会使您的模式可读(并且可能使验证更慢)。
而不是使用if
/ then
/ else
构造,我建议使用anyOf
或oneOf
,其中每个子模式代表实例的有效状态,给定one
的变化值。
{
"oneOf": [
{
"properties": {
"one": {"const": "yes"},
"two": ... // do some assertions on the two property here
}
},
{
"properties": {
"one": {"const": "no"},
"two": ... // do some assertions on the two property here
}
},
{
"properties": {
"one": {"const": "maybe"},
"two": ... // do some assertions on the two property here
}
}
]
}
在我看来,这更清洁。
希望这个解释可以帮助您重构模式以允许其他实例通过。
您必须将条件移动到足够高的级别才能引用它需要引用的所有属性。在这种情况下,这是/definitions/Base
架构。然后你只需要正确编写你的模式,如Relequestual解释的那样。
{
"$ref": "#/definitions/Base",
"definitions": {
"Base": {
"type": "object",
"properties": {
"session": { "$ref": "#/definitions/Session" },
"sdk": { "$ref": "#/definitions/SDK" }
},
"allOf": [
{
"if": {
"properties": {
"sdk": {
"properties": {
"name": { "const": "ios" }
}
}
},
"required": ["sdk"]
},
"then": {
"properties": {
"session": {
"properties": {
"session_id": { "pattern": "A" }
}
}
}
},
"else": {
"properties": {
"session": {
"properties": {
"session_id": { "pattern": "B" }
}
}
}
}
}
]
},
...
}
if
的值必须是JSON Schema。如果您要使用https://gist.github.com/Relequestual/f225c34f6becba09a2bcaa66205f47f3#file-schema-json-L29-L35(29-35)并将其用作JSON模式,则不会强制使用验证约束,因为在对象的顶层没有JSON模式关键字。
{
"SDK": {
"properties": {
"name": {
"enum": "ios"
}
}
}
}
这在规范中是允许的,因为人们可能希望通过添加自己的关键字来扩展JSON Schema的功能。所以它是“有效的”JSON Schema,但实际上并没有做任何事情。
您需要将properties
添加到架构中才能使其有意义。
{
"properties": {
"SDK": {
"properties": {
"name": {
"const": "ios"
}
}
}
}
}
另外,enum
必须是一个数组。如果您只有一个项目,则可以使用const
。