在基于另一个模式对象的 json 模式上使用条件语句
Use conditional statements on json schema based on another schema object
我有一个 json 对象,例如:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
sdk name
可以是 android or ios
。 session_id
是基于 sdk json
中的 name field
。我写了一个json schema
using conditional statement (Using draft 7) 如下:
但它以一种意想不到的方式工作:
{
"$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"
}
}
if
的值必须是 JSON 架构。如果您要使用 https://gist.github.com/Relequestual/f225c34f6becba09a2bcaa66205f47f3#file-schema-json-L29-L35 (29-35) 行并将其单独用作 JSON Schema,则不会施加任何验证约束,因为没有 JSON Schema 关键字对象的顶层。
{
"SDK": {
"properties": {
"name": {
"enum": "ios"
}
}
}
}
规范中允许这样做,因为人们可能希望通过添加自己的关键字来扩展 JSON Schema 的功能。所以它是 "valid" JSON 架构,但实际上没有做任何事情。
您需要将 properties
添加到架构中才能使其有意义。
{
"properties": {
"SDK": {
"properties": {
"name": {
"const": "ios"
}
}
}
}
}
另外,enum
必须是一个数组。当你只有一个项目时,你可以使用 const
.
我认为您遇到了与 中类似的问题。
@Relequestual 是正确的,因为您需要在 SDK
标注周围使用 properties
关键字。但是为了你想做的事情,你需要重新组织。
子模式仅在实例中的级别上运行,而不是在根级别上运行。
考虑这个模式,一个简单的 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": {
...
}
}
}
}
two
属性下的if
关键字只能考虑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
的嵌套也会增加,这会使您的架构难以阅读(并可能使验证速度变慢)。
我建议使用 anyOf
或 oneOf
,而不是使用 if
/then
/else
结构,其中每个子模式代表一个有效状态例如,给定 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" }
}
}
}
}
}
]
},
...
}
我有一个 json 对象,例如:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
sdk name
可以是 android or ios
。 session_id
是基于 sdk json
中的 name field
。我写了一个json schema
using conditional statement (Using draft 7) 如下:
但它以一种意想不到的方式工作:
{
"$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"
}
}
if
的值必须是 JSON 架构。如果您要使用 https://gist.github.com/Relequestual/f225c34f6becba09a2bcaa66205f47f3#file-schema-json-L29-L35 (29-35) 行并将其单独用作 JSON Schema,则不会施加任何验证约束,因为没有 JSON Schema 关键字对象的顶层。
{
"SDK": {
"properties": {
"name": {
"enum": "ios"
}
}
}
}
规范中允许这样做,因为人们可能希望通过添加自己的关键字来扩展 JSON Schema 的功能。所以它是 "valid" JSON 架构,但实际上没有做任何事情。
您需要将 properties
添加到架构中才能使其有意义。
{
"properties": {
"SDK": {
"properties": {
"name": {
"const": "ios"
}
}
}
}
}
另外,enum
必须是一个数组。当你只有一个项目时,你可以使用 const
.
我认为您遇到了与
@Relequestual 是正确的,因为您需要在 SDK
标注周围使用 properties
关键字。但是为了你想做的事情,你需要重新组织。
子模式仅在实例中的级别上运行,而不是在根级别上运行。
考虑这个模式,一个简单的 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": {
...
}
}
}
}
two
属性下的if
关键字只能考虑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
的嵌套也会增加,这会使您的架构难以阅读(并可能使验证速度变慢)。
我建议使用 anyOf
或 oneOf
,而不是使用 if
/then
/else
结构,其中每个子模式代表一个有效状态例如,给定 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" }
}
}
}
}
}
]
},
...
}