JSON-schema 错误的成功验证
Erroneous successful validation by JSON-schema
节点中的字段取决于实体的值。也就是说,如果 entity = "pd",那么节点有一些字段,而 entity = "top" - 节点有完全不同的字段,尽管它们是严格要求的。出于某种原因,JSON 字符串被有效模式接受,即使没有按要求在节点中定义字段。我已经整个脑袋都碎了,最多的方案哪里会出错?
JSON-架构:
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/root.json",
"type": "object",
"title": "The Root Schema",
"required": [
"virtual"
],
"properties": {
"virtual": {
"$id": "#/properties/virtual",
"type": "array",
"title": "The Virtual Schema",
"items": {
"$id": "#/properties/virtual/items",
"type": "object",
"title": "The Items Schema",
"required": [
"type",
"path",
"entity",
"nodes"
],
"properties": {
"type": {
"$id": "#/properties/virtual/items/properties/type",
"type": "string",
"title": "The Type Schema",
"default": "",
"examples": [
"bus"
],
"pattern": "^(.*)$"
},
"path": {
"$id": "#/properties/virtual/items/properties/path",
"type": "string",
"title": "The Path Schema",
"default": "",
"examples": [
"VBUS2"
],
"pattern": "^(.*)$"
},
"entity": {
"$id": "#/properties/virtual/items/properties/entity",
"type": "string",
"title": "The Entity Schema",
"default": "",
"examples": [
"topaz"
],
"enum": ["pde", "topaz"],
"pattern": "^(.*)$"
},
"nodes": {
"$id": "#/properties/virtual/items/properties/nodes",
"type": "array",
"title": "The Nodes Schema",
"items": {
"$id": "#/properties/virtual/items/properties/nodes/items",
"type": "object",
"title": "The Items Schema"
}
}
}
}
}
},
"anyOf": [
{
"if": {
"properties": { "virtual": { "properties": { "entity": { "const": "topaz" } } } }
},
"then": {
"properties": {
"virtual": {
"properties": {
"nodes": {
"items": {
"required": [
"uid",
"utype",
"uaddress",
"unozzles"
],
"properties": {
"uid": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/uid",
"type": "integer",
"title": "The Uid Schema",
"default": 0,
"examples": [
1
]
},
"utype": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/utype",
"type": "string",
"title": "The Utype Schema",
"default": "",
"examples": [
"dispenser"
],
"pattern": "^(.*)$"
},
"uaddress": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/uaddress",
"type": "string",
"title": "The Uaddress Schema",
"default": "",
"examples": [
"false"
],
"pattern": "^(.*)$"
},
"unozzles": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/unozzles",
"type": "boolean",
"title": "The Unozzles Schema",
"default": false,
"examples": [
false
]
}
}
}
}
}
}
}
}
},
{
"if": {
"properties": { "virtual": { "properties": { "entity": { "const" : "pde" } } } }
},
"then": {
"properties": {
"virtual": {
"properties": {
"nodes": {
"items": {
"required": [
"id",
"type",
"address",
"nozzles"
],
"properties": {
"id": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/id",
"type": "string",
"title": "The Id Schema",
"default": "",
"examples": [
"vrt_1"
],
"pattern": "^(.*)$"
},
"type": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/type",
"type": "string",
"title": "The Type Schema",
"default": "",
"examples": [
"dispenser"
],
"pattern": "^(.*)$"
},
"address": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/address",
"type": "integer",
"title": "The Address Schema",
"default": 0,
"examples": [
1
]
},
"nozzles": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/nozzles",
"type": "array",
"title": "The Nozzles Schema",
"items": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/nozzles/items",
"type": "integer",
"title": "The Items Schema",
"default": 0,
"examples": [
1,
2,
3
]
}
}
}
}
}
}
}
}
}
}
]
}
这个JSON有效:
{
"virtual": [
{
"type": "bus",
"path": "VUS1",
"entity": "pde",
"nodes": [
{
"id": "vrt_1",
"type": "string",
"address": 1,
"nozzles": [1, 2, 3]
},
{
"id": "vrt_2",
"type": "string",
"address": 2,
"nozzles": [1, 2, 3]
}
]
},
{
"type": "bus",
"path": "VUS2",
"entity": "topaz",
"nodes": [
{
"uid": 1,
"utype": "string",
"uaddress": "false",
"unozzles": false
},
{
"uid": "vrt_1",
"utype": "string",
"uaddress": "false",
"unozzles": false
}
]
}
]
}
并且这个 JSON 不应该应用,但被认为是有效的:
{
"virtual": [
{
"type": "bus",
"path": "VUS1",
"entity": "pde",
"nodes": [
{
"id_not_valid": "failure",
"type": 1,
"address": false,
"nozzles": [1, 2, 3]
},
{
"id": "vrt_2",
"type": "string",
"address": false,
"nozzles": [1, 2, 3]
}
]
},
{
"type": "bus",
"path": "VUS2",
"entity": "topaz",
"nodes": [
{
"uid_not_valid": "failure",
"utype": 1,
"uaddress": "false",
"unozzles": false
}
]
}
]
}
理论上,第二个JSON不应该被验证。有几个原因:
- 对于实体= "pd",必填字段为"id"、"type"、“地址"and"喷嘴”。在 JSON 的第二行中,字段 "id" 被字段 "id_not_valid" 替换 - > 必填字段“ id ”不存在,验证必须以失败告终。 entity="top" - "the uid" 被替换为 "id_not_valid"
- 对于entity="pd",address字段是token类型,在第2JSON行设置为false,对应"boolean"类型,但是校验仍然发生(如果您将数组或字符串值分配给地址,则相同)。对于entity="top"类型,类型是string,但是赋给它的整数值1也被验证器假定为正确的string。
但是下面链接上的在线验证器说一切正常,JSON 都符合方案。
所以我认为这个方案有错误。
该方案本身就是由这个例子制定的 Example of JSON schema compilation
任何关于修复 JSON-schema 的评论和提示,请
架构 格式错误。
(我忽略了一个事实,即模式声明 entity
应该是 "pde"
或 "topaz"
,但实例有 "pd"
和 "top"
.我认为这是一个错字。)
在 anyOf
中,您有两个项目,每个项目都有一个 if
条件关键字。该关键字呈现的架构是
{
"properties": {
"virtual": {
"properties": {
"entity": {
"const": "topaz"
}
}
}
}
}
这是说如果virtual
有一个entity
属性,那么应该是"topaz"
。但是 properties
的工作方式是,如果实例是一个对象 ,它只会失败验证 。但是在 #/properties
中,您声明 virtual
应该是一个对象数组,其中 每个项目 包含一个 entity
属性。
由于 virtual
在您的实例中是一个数组,因此 anyOf
传递中 if
条件关键字的 none,因此它们遵循 else
那些不存在的子模式的关键字(因此默认情况下通过)。这导致 anyOf
通过的两个子模式。
我 认为 您要做的是根据 entity
属性 的值验证数组中的每个项目那个项目。这意味着您可以在数组中同时拥有 pde
项和 topaz
项。
为此,您需要隔离方差所在的位置。在您的例子中,它是 virtual
数组中的项目级别。这是您需要放置 anyOf
.
的地方
因此您需要将 anyOf
添加到 #/properties/virtual/items
。这是架构中 if
/then
构造可以关闭 entity
属性 并强制执行 nodes
属性 的唯一一点.
编辑 我要更改的内容
- 删除所有内部
$id
声明。它们只是重申了文档中的位置,并没有提供额外的功能。
- 从
entity
中删除 type
和 pattern
声明。 enum
在这里就足够了,因为它声明值必须是数组中的一项。由于这些都是字符串并且匹配给定的模式,因此这些关键字是多余的。
- 将
anyOf
移动到 virtual
内的 properties
关键字旁边,并将其更改为 oneOf
。这是您可以访问 entities
属性 和 nodes
属性 的最具体的位置。将其更改为 oneOf
可确保恰好有一个为真。
- 删除
if
/then
结构,只在 then
部分包含常量值。
最后,它的结构如下:
{
... ,
"properties": {
"virtual": {
"type": "array",
"title": "The Virtual Schema",
"items": {
"type": "object",
"title": "The Items Schema",
"required": [ "type", "path", "entity", "nodes" ],
"properties": {
"type": { ... },
"path": { ... },
"entity": {
"title": "The Entity Schema",
"default": "",
"examples": [
"topaz"
],
"enum": ["pde", "topaz"]
}
},
"oneOf": [
{
"properties": {
"entity": {"const": "topaz"},
"nodes": { ... }
}
},
{
"properties": {
"entity": {"const": "pde"},
"nodes": { ... }
}
}
]
}
}
}
}
在这里,我们声明 virtual
数组中的项目必须是需要 4 个属性的对象:type
、path
、entity
和 nodes
。我们使用 properties
关键字显式定义 type
、path
、entity
。但是我们有条件地使用 oneOf
定义 nodes
属性 并在每种情况下为 entity
属性 指定一个常数值。
节点中的字段取决于实体的值。也就是说,如果 entity = "pd",那么节点有一些字段,而 entity = "top" - 节点有完全不同的字段,尽管它们是严格要求的。出于某种原因,JSON 字符串被有效模式接受,即使没有按要求在节点中定义字段。我已经整个脑袋都碎了,最多的方案哪里会出错? JSON-架构:
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/root.json",
"type": "object",
"title": "The Root Schema",
"required": [
"virtual"
],
"properties": {
"virtual": {
"$id": "#/properties/virtual",
"type": "array",
"title": "The Virtual Schema",
"items": {
"$id": "#/properties/virtual/items",
"type": "object",
"title": "The Items Schema",
"required": [
"type",
"path",
"entity",
"nodes"
],
"properties": {
"type": {
"$id": "#/properties/virtual/items/properties/type",
"type": "string",
"title": "The Type Schema",
"default": "",
"examples": [
"bus"
],
"pattern": "^(.*)$"
},
"path": {
"$id": "#/properties/virtual/items/properties/path",
"type": "string",
"title": "The Path Schema",
"default": "",
"examples": [
"VBUS2"
],
"pattern": "^(.*)$"
},
"entity": {
"$id": "#/properties/virtual/items/properties/entity",
"type": "string",
"title": "The Entity Schema",
"default": "",
"examples": [
"topaz"
],
"enum": ["pde", "topaz"],
"pattern": "^(.*)$"
},
"nodes": {
"$id": "#/properties/virtual/items/properties/nodes",
"type": "array",
"title": "The Nodes Schema",
"items": {
"$id": "#/properties/virtual/items/properties/nodes/items",
"type": "object",
"title": "The Items Schema"
}
}
}
}
}
},
"anyOf": [
{
"if": {
"properties": { "virtual": { "properties": { "entity": { "const": "topaz" } } } }
},
"then": {
"properties": {
"virtual": {
"properties": {
"nodes": {
"items": {
"required": [
"uid",
"utype",
"uaddress",
"unozzles"
],
"properties": {
"uid": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/uid",
"type": "integer",
"title": "The Uid Schema",
"default": 0,
"examples": [
1
]
},
"utype": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/utype",
"type": "string",
"title": "The Utype Schema",
"default": "",
"examples": [
"dispenser"
],
"pattern": "^(.*)$"
},
"uaddress": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/uaddress",
"type": "string",
"title": "The Uaddress Schema",
"default": "",
"examples": [
"false"
],
"pattern": "^(.*)$"
},
"unozzles": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/unozzles",
"type": "boolean",
"title": "The Unozzles Schema",
"default": false,
"examples": [
false
]
}
}
}
}
}
}
}
}
},
{
"if": {
"properties": { "virtual": { "properties": { "entity": { "const" : "pde" } } } }
},
"then": {
"properties": {
"virtual": {
"properties": {
"nodes": {
"items": {
"required": [
"id",
"type",
"address",
"nozzles"
],
"properties": {
"id": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/id",
"type": "string",
"title": "The Id Schema",
"default": "",
"examples": [
"vrt_1"
],
"pattern": "^(.*)$"
},
"type": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/type",
"type": "string",
"title": "The Type Schema",
"default": "",
"examples": [
"dispenser"
],
"pattern": "^(.*)$"
},
"address": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/address",
"type": "integer",
"title": "The Address Schema",
"default": 0,
"examples": [
1
]
},
"nozzles": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/nozzles",
"type": "array",
"title": "The Nozzles Schema",
"items": {
"$id": "#/properties/virtual/items/properties/nodes/items/properties/nozzles/items",
"type": "integer",
"title": "The Items Schema",
"default": 0,
"examples": [
1,
2,
3
]
}
}
}
}
}
}
}
}
}
}
]
}
这个JSON有效:
{
"virtual": [
{
"type": "bus",
"path": "VUS1",
"entity": "pde",
"nodes": [
{
"id": "vrt_1",
"type": "string",
"address": 1,
"nozzles": [1, 2, 3]
},
{
"id": "vrt_2",
"type": "string",
"address": 2,
"nozzles": [1, 2, 3]
}
]
},
{
"type": "bus",
"path": "VUS2",
"entity": "topaz",
"nodes": [
{
"uid": 1,
"utype": "string",
"uaddress": "false",
"unozzles": false
},
{
"uid": "vrt_1",
"utype": "string",
"uaddress": "false",
"unozzles": false
}
]
}
]
}
并且这个 JSON 不应该应用,但被认为是有效的:
{
"virtual": [
{
"type": "bus",
"path": "VUS1",
"entity": "pde",
"nodes": [
{
"id_not_valid": "failure",
"type": 1,
"address": false,
"nozzles": [1, 2, 3]
},
{
"id": "vrt_2",
"type": "string",
"address": false,
"nozzles": [1, 2, 3]
}
]
},
{
"type": "bus",
"path": "VUS2",
"entity": "topaz",
"nodes": [
{
"uid_not_valid": "failure",
"utype": 1,
"uaddress": "false",
"unozzles": false
}
]
}
]
}
理论上,第二个JSON不应该被验证。有几个原因:
- 对于实体= "pd",必填字段为"id"、"type"、“地址"and"喷嘴”。在 JSON 的第二行中,字段 "id" 被字段 "id_not_valid" 替换 - > 必填字段“ id ”不存在,验证必须以失败告终。 entity="top" - "the uid" 被替换为 "id_not_valid"
- 对于entity="pd",address字段是token类型,在第2JSON行设置为false,对应"boolean"类型,但是校验仍然发生(如果您将数组或字符串值分配给地址,则相同)。对于entity="top"类型,类型是string,但是赋给它的整数值1也被验证器假定为正确的string。
但是下面链接上的在线验证器说一切正常,JSON 都符合方案。
所以我认为这个方案有错误。
该方案本身就是由这个例子制定的 Example of JSON schema compilation
任何关于修复 JSON-schema 的评论和提示,请
架构 格式错误。
(我忽略了一个事实,即模式声明 entity
应该是 "pde"
或 "topaz"
,但实例有 "pd"
和 "top"
.我认为这是一个错字。)
在 anyOf
中,您有两个项目,每个项目都有一个 if
条件关键字。该关键字呈现的架构是
{
"properties": {
"virtual": {
"properties": {
"entity": {
"const": "topaz"
}
}
}
}
}
这是说如果virtual
有一个entity
属性,那么应该是"topaz"
。但是 properties
的工作方式是,如果实例是一个对象 ,它只会失败验证 。但是在 #/properties
中,您声明 virtual
应该是一个对象数组,其中 每个项目 包含一个 entity
属性。
由于 virtual
在您的实例中是一个数组,因此 anyOf
传递中 if
条件关键字的 none,因此它们遵循 else
那些不存在的子模式的关键字(因此默认情况下通过)。这导致 anyOf
通过的两个子模式。
我 认为 您要做的是根据 entity
属性 的值验证数组中的每个项目那个项目。这意味着您可以在数组中同时拥有 pde
项和 topaz
项。
为此,您需要隔离方差所在的位置。在您的例子中,它是 virtual
数组中的项目级别。这是您需要放置 anyOf
.
因此您需要将 anyOf
添加到 #/properties/virtual/items
。这是架构中 if
/then
构造可以关闭 entity
属性 并强制执行 nodes
属性 的唯一一点.
编辑 我要更改的内容
- 删除所有内部
$id
声明。它们只是重申了文档中的位置,并没有提供额外的功能。 - 从
entity
中删除type
和pattern
声明。enum
在这里就足够了,因为它声明值必须是数组中的一项。由于这些都是字符串并且匹配给定的模式,因此这些关键字是多余的。 - 将
anyOf
移动到virtual
内的properties
关键字旁边,并将其更改为oneOf
。这是您可以访问entities
属性 和nodes
属性 的最具体的位置。将其更改为oneOf
可确保恰好有一个为真。 - 删除
if
/then
结构,只在then
部分包含常量值。
最后,它的结构如下:
{
... ,
"properties": {
"virtual": {
"type": "array",
"title": "The Virtual Schema",
"items": {
"type": "object",
"title": "The Items Schema",
"required": [ "type", "path", "entity", "nodes" ],
"properties": {
"type": { ... },
"path": { ... },
"entity": {
"title": "The Entity Schema",
"default": "",
"examples": [
"topaz"
],
"enum": ["pde", "topaz"]
}
},
"oneOf": [
{
"properties": {
"entity": {"const": "topaz"},
"nodes": { ... }
}
},
{
"properties": {
"entity": {"const": "pde"},
"nodes": { ... }
}
}
]
}
}
}
}
在这里,我们声明 virtual
数组中的项目必须是需要 4 个属性的对象:type
、path
、entity
和 nodes
。我们使用 properties
关键字显式定义 type
、path
、entity
。但是我们有条件地使用 oneOf
定义 nodes
属性 并在每种情况下为 entity
属性 指定一个常数值。