Почему моя 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 условие не выполнено.

Другие вопросы по тегам