我有一个像这样的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"
}
}
有人可以解释y吗?。即使通过了:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
答案 0 :(得分:3)
我认为您遇到的问题与this question中的问题类似。
@Relequestual是正确的,因为您需要在properties
标注周围使用SDK
关键字。但是对于您想做的事情,您需要进行重组。
子计划仅在实例级别运行,而不是在根级别运行。
将此模式考虑为一个简单的JSON对象实例,该实例包含一个one
和一个two
属性:
{
"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
的嵌套也会增加,这会使您的架构难以阅读(并可能使验证变慢)。
我建议不要使用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
}
}
]
}
我认为这要干净得多。
希望该解释有助于您重构架构,以允许其他实例通过。
答案 1 :(得分:2)
您必须将条件移到足够高的级别,才能引用其需要引用的所有属性。在这种情况下,这就是/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" }
}
}
}
}
}
]
},
...
}
答案 2 :(得分:0)
if
的值必须是JSON模式。如果要使用https://gist.github.com/Relequestual/f225c34f6becba09a2bcaa66205f47f3#file-schema-json-L29-L35行(29-35)并将其单独用作JSON模式,则不会施加任何验证约束,因为在对象的顶层没有JSON模式关键字。 / p>
{
"SDK": {
"properties": {
"name": {
"enum": "ios"
}
}
}
}
规范中允许这样做,因为人们可能想通过添加自己的关键字来扩展JSON Schema的功能。因此,它是“有效的” JSON模式,但实际上并没有做任何事情。
您需要在模式中添加properties
才有意义。
{
"properties": {
"SDK": {
"properties": {
"name": {
"const": "ios"
}
}
}
}
}
此外,enum
必须是一个数组。当您只有一个项目时,可以使用const
。