为什么我使用的 JSON Schema `allOf` 关键字没有正确验证?
Why is my use of JSON Schema `allOf` keyword not validating correctly?
我 运行 有更多未正确验证的子对象(object.actor、object.verb、object.object)。在将对象架构中的 if/then 结构更改为 if/then/else 后,我尝试寻找任何空架构,并为每个其他架构添加 false 值。我没有发现任何明显的东西。
JSON - 应该失败但没有
{
"actor": {
"objectType": "Agent",
"name": "xAPI account",
"mbox": "mailto:xapi@adlnet.gov"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attended",
"display": {
"en-GB": "attended",
"en-US": "attended"
}
},
"object": {
"objectType": "SubStatement",
"actor": {
"objectType": "should fail",
"name": "xAPI mbox",
"mbox": "mailto:should fail"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/reported",
"display": {
"should fail": "reported",
"en-US": "reported"
}
},
"object": {
"objectType": "Activity",
"id": "should fail"
}
}
}
JSON - 仅在根级别失败;不检查子语句值。假设来自某处的空模式。
{
"actor": {
"objectType": "Agent",
"name": "xAPI account",
"mbox": "this fails"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attended",
"display": {
"this fails": "attended",
"en-US": "attended"
}
},
"object": {
"objectType": "SubStatement",
"actor": {
"objectType": "should fail",
"name": "xAPI mbox",
"mbox": "mailto:should fail"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/reported",
"display": {
"should fail": "reported",
"en-US": "reported"
}
},
"object": {
"objectType": "Activity",
"id": "should fail"
}
}
}
JSON SCHEMA(精简)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "xAPIValidator",
"description": "Validation schema for xAPI tests",
"type": "object",
"allOf": [
{
"$ref": "#/definitions/Statement"
},
{
"statements": [
{
"$ref": "#/definitions/Statement"
}
]
}
],
"definitions": {
"Statement": {
"additionalProperties": false,
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Agent",
"Activity",
"Group",
"SubStatement",
"StatementRef"
]
},
"id": {
"allOf": [
{
"$ref": "#/definitions/uuid"
}
]
},
"actor": {
"$id": "#actor",
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
},
"verb": {
"$id": "#verb",
"type": "object",
"properties": {
"id": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
},
"display": {
"type": "object",
"allOf": [
{
"$ref": "#/definitions/lang5646"
}
]
}
}
},
"object": {
"$id": "#object",
"type": "object",
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Activity",
"Agent",
"Group",
"SubStatement",
"StatementRef"
]
}
},
"oneOf": [
{
"if": {
"properties": {
"objectType": {
"const": "SubStatement"
}
}
},
"then": {
"$comment": "Substatement object type",
"allOf": [
{
"$ref": "#/definitions/Statement"
}
],
"allOf": [
{
"not": {
"required": [
"id"
]
}
},
{
"not": {
"required": [
"authority"
]
}
},
{
"not": {
"required": [
"stored"
]
}
},
{"required":["actor","verb","object"]}
]
},
"else": false
}
]
}
},
"required": [
"actor",
"verb",
"object"
]
},
"Agent": {
"$id": "#Agent",
"maxProperties": 3,
"allOf": [
{
"$ref": "#/definitions/IFI"
}
]
},
"AnonGroup": {
"$id": "#AnonGroup",
"maxProperties": 3,
"properties": {
"member": {
"type": "array",
"items": [
{
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
}
]
}
},
"dependencies": {
"objectType": [
"member"
]
},
"required": [
"member"
],
"not": {
"required": [
"mbox"
]
},
"not": {
"required": [
"mbox_sha1sum"
]
},
"not": {
"required": [
"openid"
]
},
"not": {
"required": [
"account"
]
}
},
"IdGroup": {
"$id": "#IdGroup",
"maxProperties": 4,
"properties": {
"member": {
"type": "array",
"items": [
{
"oneOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
}
]
}
},
"oneOf": [
{
"$ref": "#/definitions/IFI"
}
]
},
"allOfAgentGroup": {
"properties": {
"objectType": {
"type": "string",
"enum": [
"Agent",
"Group"
]
},
"name": {
"type": "string"
}
},
"oneOf": [
{
"if": {
"properties": {
"objectType": {
"const": "Agent"
}
}
},
"then": {
"allOf": [
{
"$ref": "#/definitions/Agent"
}
]
},
"else": false
},
{
"if": {
"properties": {
"objectType": {
"const": "Group"
}
}
},
"then": {
"oneOf": [
{
"allOf": [
{
"$ref": "#/definitions/IdGroup"
}
]
},
{
"allOf": [
{
"$ref": "#/definitions/AnonGroup"
}
]
}
]
},
"else": false
}
]
},
"IFI": {
"oneOf": [
{
"properties": {
"mbox": {
"allOf": [
{
"$ref": "#/definitions/mailto"
}
]
}
},
"required": [
"mbox"
]
},
{
"properties": {
"mbox_sha1sum": {
"type": "string",
"pattern": "\b[0-9a-f]{5,40}\b"
}
},
"required": [
"mbox_sha1sum"
]
},
{
"properties": {
"account": {
"properties": {
"homePage": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
},
"name": {
"type": "string"
}
},
"required": [
"homePage",
"name"
]
}
},
"required": [
"account"
]
},
{
"properties": {
"openid": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
}
},
"required": [
"openid"
]
}
]
},
"mailto": {
"type": "string",
"pattern": "(mailto:)(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"
},
"URI": {
"type": "string",
"pattern": "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"
},
"uuid": {
"type": "string",
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
},
"lang5646": {
"type": "object",
"patternProperties": {
"^((?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?:([A-Za-z]{2,3}(-(?:[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?:[A-Za-z]{4}))?(-(?:[A-Za-z]{2}|[0-9]{3}))?(-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?:[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?:x(-[A-Za-z0-9]{1,8})+))?)|(?:x(-[A-Za-z0-9]{1,8})+))$": {
"type": "string"
}
},
"additionalProperties": false
},
"lang5646string": {
"type": "string",
"pattern": "^((?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?:([A-Za-z]{2,3}(-(?:[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?:[A-Za-z]{4}))?(-(?:[A-Za-z]{2}|[0-9]{3}))?(-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?:[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?:x(-[A-Za-z0-9]{1,8})+))?)|(?:x(-[A-Za-z0-9]{1,8})+))$"
}
}
}
在 "$id": "#object"
> oneOf > 然后,您在 JSON 对象中定义了两次 allOf
。
JSON 中重复键的行为未定义。
通常它的处理方式只是获取对象的每个键的最后一次出现。
您可以通过具有以下架构和一个空对象实例来查看此工作:{}
架构:
{
"allOf": [
false
],
"allOf": [
true
]
}
JSON 库可能会忽略第一个 allOf
,最后一次出现,导致始终通过验证。交换 true
和 false
以双重确认。
只要您在架构中的同一个对象中多次使用特定 *of
关键字(或在一个对象中多次使用任何键),您就需要修复它。
我 运行 有更多未正确验证的子对象(object.actor、object.verb、object.object)。在将对象架构中的 if/then 结构更改为 if/then/else 后,我尝试寻找任何空架构,并为每个其他架构添加 false 值。我没有发现任何明显的东西。
JSON - 应该失败但没有
{
"actor": {
"objectType": "Agent",
"name": "xAPI account",
"mbox": "mailto:xapi@adlnet.gov"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attended",
"display": {
"en-GB": "attended",
"en-US": "attended"
}
},
"object": {
"objectType": "SubStatement",
"actor": {
"objectType": "should fail",
"name": "xAPI mbox",
"mbox": "mailto:should fail"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/reported",
"display": {
"should fail": "reported",
"en-US": "reported"
}
},
"object": {
"objectType": "Activity",
"id": "should fail"
}
}
}
JSON - 仅在根级别失败;不检查子语句值。假设来自某处的空模式。
{
"actor": {
"objectType": "Agent",
"name": "xAPI account",
"mbox": "this fails"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attended",
"display": {
"this fails": "attended",
"en-US": "attended"
}
},
"object": {
"objectType": "SubStatement",
"actor": {
"objectType": "should fail",
"name": "xAPI mbox",
"mbox": "mailto:should fail"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/reported",
"display": {
"should fail": "reported",
"en-US": "reported"
}
},
"object": {
"objectType": "Activity",
"id": "should fail"
}
}
}
JSON SCHEMA(精简)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "xAPIValidator",
"description": "Validation schema for xAPI tests",
"type": "object",
"allOf": [
{
"$ref": "#/definitions/Statement"
},
{
"statements": [
{
"$ref": "#/definitions/Statement"
}
]
}
],
"definitions": {
"Statement": {
"additionalProperties": false,
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Agent",
"Activity",
"Group",
"SubStatement",
"StatementRef"
]
},
"id": {
"allOf": [
{
"$ref": "#/definitions/uuid"
}
]
},
"actor": {
"$id": "#actor",
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
},
"verb": {
"$id": "#verb",
"type": "object",
"properties": {
"id": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
},
"display": {
"type": "object",
"allOf": [
{
"$ref": "#/definitions/lang5646"
}
]
}
}
},
"object": {
"$id": "#object",
"type": "object",
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Activity",
"Agent",
"Group",
"SubStatement",
"StatementRef"
]
}
},
"oneOf": [
{
"if": {
"properties": {
"objectType": {
"const": "SubStatement"
}
}
},
"then": {
"$comment": "Substatement object type",
"allOf": [
{
"$ref": "#/definitions/Statement"
}
],
"allOf": [
{
"not": {
"required": [
"id"
]
}
},
{
"not": {
"required": [
"authority"
]
}
},
{
"not": {
"required": [
"stored"
]
}
},
{"required":["actor","verb","object"]}
]
},
"else": false
}
]
}
},
"required": [
"actor",
"verb",
"object"
]
},
"Agent": {
"$id": "#Agent",
"maxProperties": 3,
"allOf": [
{
"$ref": "#/definitions/IFI"
}
]
},
"AnonGroup": {
"$id": "#AnonGroup",
"maxProperties": 3,
"properties": {
"member": {
"type": "array",
"items": [
{
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
}
]
}
},
"dependencies": {
"objectType": [
"member"
]
},
"required": [
"member"
],
"not": {
"required": [
"mbox"
]
},
"not": {
"required": [
"mbox_sha1sum"
]
},
"not": {
"required": [
"openid"
]
},
"not": {
"required": [
"account"
]
}
},
"IdGroup": {
"$id": "#IdGroup",
"maxProperties": 4,
"properties": {
"member": {
"type": "array",
"items": [
{
"oneOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
}
]
}
},
"oneOf": [
{
"$ref": "#/definitions/IFI"
}
]
},
"allOfAgentGroup": {
"properties": {
"objectType": {
"type": "string",
"enum": [
"Agent",
"Group"
]
},
"name": {
"type": "string"
}
},
"oneOf": [
{
"if": {
"properties": {
"objectType": {
"const": "Agent"
}
}
},
"then": {
"allOf": [
{
"$ref": "#/definitions/Agent"
}
]
},
"else": false
},
{
"if": {
"properties": {
"objectType": {
"const": "Group"
}
}
},
"then": {
"oneOf": [
{
"allOf": [
{
"$ref": "#/definitions/IdGroup"
}
]
},
{
"allOf": [
{
"$ref": "#/definitions/AnonGroup"
}
]
}
]
},
"else": false
}
]
},
"IFI": {
"oneOf": [
{
"properties": {
"mbox": {
"allOf": [
{
"$ref": "#/definitions/mailto"
}
]
}
},
"required": [
"mbox"
]
},
{
"properties": {
"mbox_sha1sum": {
"type": "string",
"pattern": "\b[0-9a-f]{5,40}\b"
}
},
"required": [
"mbox_sha1sum"
]
},
{
"properties": {
"account": {
"properties": {
"homePage": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
},
"name": {
"type": "string"
}
},
"required": [
"homePage",
"name"
]
}
},
"required": [
"account"
]
},
{
"properties": {
"openid": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
}
},
"required": [
"openid"
]
}
]
},
"mailto": {
"type": "string",
"pattern": "(mailto:)(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"
},
"URI": {
"type": "string",
"pattern": "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"
},
"uuid": {
"type": "string",
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
},
"lang5646": {
"type": "object",
"patternProperties": {
"^((?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?:([A-Za-z]{2,3}(-(?:[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?:[A-Za-z]{4}))?(-(?:[A-Za-z]{2}|[0-9]{3}))?(-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?:[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?:x(-[A-Za-z0-9]{1,8})+))?)|(?:x(-[A-Za-z0-9]{1,8})+))$": {
"type": "string"
}
},
"additionalProperties": false
},
"lang5646string": {
"type": "string",
"pattern": "^((?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?:([A-Za-z]{2,3}(-(?:[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?:[A-Za-z]{4}))?(-(?:[A-Za-z]{2}|[0-9]{3}))?(-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?:[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?:x(-[A-Za-z0-9]{1,8})+))?)|(?:x(-[A-Za-z0-9]{1,8})+))$"
}
}
}
在 "$id": "#object"
> oneOf > 然后,您在 JSON 对象中定义了两次 allOf
。
JSON 中重复键的行为未定义。 通常它的处理方式只是获取对象的每个键的最后一次出现。
您可以通过具有以下架构和一个空对象实例来查看此工作:{}
架构:
{
"allOf": [
false
],
"allOf": [
true
]
}
JSON 库可能会忽略第一个 allOf
,最后一次出现,导致始终通过验证。交换 true
和 false
以双重确认。
只要您在架构中的同一个对象中多次使用特定 *of
关键字(或在一个对象中多次使用任何键),您就需要修复它。