Почему моя JSON-схема неправильно проверяет дочерний объект?
Учитывая приведенные ниже JSON и схему, actor.mbox, actor.member[0].objectType и actor.member[0].mbox должны завершиться неудачей. Они не. Должно быть, что-то не так в моей схеме. Я думаю, что я сузил это к чему-то имеющему отношение к определению IdGroup, но я не могу найти проблему. Любой гуру JSON Schema видит что-то явно не так?
JSON
{
"actor": {
"objectType": "Group",
"name": "Group Identified",
"mbox": "http://should.fail.com",
"member": [
{
"objectType": "Agent_shouldfail",
"name": "xAPI mbox",
"mbox": "mailto:shouldfail"
}
]
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attended",
"display": {
"en-GB": "attended",
"en-US": "attended"
}
},
"object": {
"objectType": "Activity",
"id": "http://www.example.com/meetings/occurances/34534"
}
}
Схема JSON (урезанная)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "xAPIValidator",
"description": "Validation schema for xAPI tests",
"type": "object",
"allOf": [
{
"$ref": "#/definitions/Statement"
}
],
"statements": {
"type": "array",
"items": {
"allOf": [
{
"$ref": "#/definitions/Statement"
}
]
}
},
"definitions": {
"Statement": {
"$id": "#Statement",
"additionalProperties": false,
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Agent",
"Activity",
"Group",
"SubStatement",
"StatementRef"
]
},
"id": {
"allOf": [
{
"$ref": "#/definitions/uuid"
}
]
},
"timestamp": {
"allOf": [
{
"$ref": "#/definitions/timestamp"
}
]
},
"stored": {
"allOf": [
{
"$ref": "#/definitions/timestamp"
}
]
},
"version": {
"allOf": [
{
"$ref": "#/definitions/semanticVersion"
}
]
},
"actor": {
"$id": "#actor",
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
},
"authority": {
"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",
"additionalProperties": true,
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Activity",
"Agent",
"Group",
"SubStatement",
"StatementRef"
]
}
}
}
},
"required": [
"actor",
"verb",
"object"
]
},
"attachment": {
"properties": {
"usageType": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
},
"display": {
"allOf": [
{
"$ref": "#/definitions/lang5646"
}
]
},
"description": {
"allOf": [
{
"$ref": "#/definitions/lang5646"
}
]
},
"contentType": {
"type": "string",
"pattern": "\\w+/[-+.\\w]+;?(\\w+.*=\\w+;?)*"
},
"length": {
"type": "integer"
},
"sha2": {
"type": "string"
},
"fileUrl": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
}
}
},
"semanticVersion": {
"type": [
"string"
],
"pattern": "^([0-9]+)\\.([0-9]+)\\\\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+)?$"
},
"Agent": {
"$id": "#Agent",
"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",
"properties": {
"member": {
"type": "array",
"items": [
{
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
}
]
}
},
"allOf": [
{
"$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"
}
]
}
},
{
"if": {
"properties": {
"objectType": {
"const": "Group"
}
}
},
"then": {
"oneOf": [
{
"allOf": [
{
"$ref": "#/definitions/IdGroup"
}
]
},
{
"allOf": [
{
"$ref": "#/definitions/AnonGroup"
}
]
}
]
}
}
]
},
"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])+)\\])"
},
"timestamp": {
"type": "string",
"pattern": "^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$"
},
"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})+))$"
}
}
}
1 ответ
if/then/else
не совсем работает, как вы ожидаете.
В вашем определении схемы allOfAgentGroup
имеет oneOf
раздел. Давайте посмотрим на это самостоятельно.
В ваших примерах данных, которые должны потерпеть неудачу, давайте возьмем объект "актер" и сам по себе.
Схема:
{
"oneOf": [
{
"if": {
"properties": {
"objectType": {
"const": "Agent"
}
}
},
"then": false
}
},
false
]
}
Данные экземпляра:
{
"objectType": "Group",
"name": "Group Identified",
"mbox": "http://should.fail.com",
"member": [
{
"objectType": "Agent_shouldfail",
"name": "xAPI mbox",
"mbox": "mailto:shouldfail"
}
]
}
Мы знаем, что вы ожидаете второй элемент в oneOf
массив не прошел проверку. Для целей отладки и демонстрации, давайте предположим, что это не удалось, и изменим его на false
(которая является допустимой "JSON-схемой", всегда приводящей к сбою проверки в этой ветви).
Теперь, учитывая приведенную выше схему и экземпляр, можно ожидать, что данные экземпляра JSON не пройдут проверку, верно? Это не так, и на самом деле проверка проходит.
Помните, что каждое ключевое слово JSON Schema добавляет ограничения к вашим требованиям валидации. Давайте посмотрим на что if
а также then
на самом деле
if
Этот результат проверки подсхемы этого ключевого слова не имеет прямого влияния на общий результат проверки. Скорее, он контролирует, какое из ключевых слов "then" или "else" оценивается.
Экземпляры, которые успешно проверяются по подсхеме этого ключевого слова, ДОЛЖНЫ также быть действительными по отношению к значению подсхемы ключевого слова then, если оно присутствует.
http://json-schema.org/latest/json-schema-validation.html
Хорошо, так что если схема if
успешно проверяет, схема из then
применены.
Данные экземпляра не проходят проверку условия if, и поэтому... схема из then
не применяется, и в результате вы получаете "пустую схему", что означает отсутствие ограничений для проверки oneOf/0
, Это приводит к вашему oneOf
утверждение проверки успешно, потому что oneOf/0
проходит и oneOf/1
выходит из строя.
Чтобы решить эту проблему, нужно добавить "then": false
к вашему объекту, который содержит if
а также then
в том случае, если вы хотите, чтобы этот элемент схемы не прошел проверку, если if
условие не выполнено.