Почему моя схема JSON неправильно проверяет дочерний объект?

Учитывая приведенные ниже JSON и схему, всеactor.mbox,actor.member[0].objectType иactor.member[0].mboxдолжны дать сбой. Они не. Должно быть что-то не так в моей схеме. Я думаю, что сузил его до чего-то, связанного с определением IdGroup, но не могу найти проблему. Любой гуру схемы json видит что-то явно неправильное?

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})+))$"
    }
  }
}

person dmcquay    schedule 01.05.2019    source источник
comment
Да, ваша схема неверна. Я объясню...   -  person Relequestual    schedule 01.05.2019
comment
Здесь много схем. Для других, не могли бы вы рассмотреть возможность удаления частей, которые, по вашему мнению, не имеют отношения к проблеме, пожалуйста?   -  person Relequestual    schedule 01.05.2019
comment
Объекты JSON с повторяющимися ключами имеют неопределенное поведение. required принимает массив, поэтому поместите все обязательные поля в один и тот же массив (и то же самое для всех необязательных дубликатов). Тогда я, возможно, смогу лучше помочь вам здесь.   -  person Relequestual    schedule 01.05.2019
comment
Я разобрал схему. Не думай, что я вытащил что-то важное. Я смотрю на ключевой вопрос, который вы упомянули.   -  person dmcquay    schedule 01.05.2019
comment
Что касается ключей, я полагаю, вы говорите об определении AnonGroup?   -  person dmcquay    schedule 01.05.2019
comment
Возможно, но проверить сейчас не могу. Я проработал вопрос, но я не могу предоставить его позже. Только на мобиле сейчас.   -  person Relequestual    schedule 01.05.2019


Ответы (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 добавляет ограничения к вашим требованиям проверки. Давайте посмотрим, что на самом деле ДЕЛАЮТ if и then.

if

Этот результат проверки подсхемы этого ключевого слова не оказывает прямого влияния на общий результат проверки. Скорее, он контролирует, какие из ключевых слов «тогда» или «иначе» оцениваются.

Экземпляры, которые успешно прошли проверку на соответствие подсхеме этого ключевого слова, ДОЛЖНЫ также быть действительными по отношению к значению подсхемы ключевого слова «тогда», если оно присутствует.

http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.6

Итак, если схема if успешно проходит проверку, применяется схема из then.

Данные экземпляра не проходят проверку условия if, и поэтому... схема из then не применяется, и вы получаете то, что фактически является «пустой схемой», что означает отсутствие ограничений проверки для oneOf/0. Это приводит к тому, что ваше oneOf утверждает, что проверка прошла успешно, потому что oneOf/0 проходит, а oneOf/1 терпит неудачу.

Чтобы решить эту проблему, вам нужно добавить "then": false к вашему объекту, который содержит if и then, в случае, если вы хотите, чтобы этот элемент схемы не прошел проверку, если условие if не выполнено.

person Relequestual    schedule 02.05.2019
comment
Понимание того, как ключевые слова аппликатора работают с булевой логикой, является обычным камнем преткновения для тех, кто пишет расширенные или сложные схемы. Ты отлично справляешься! знак равно - person Relequestual; 02.05.2019
comment
Думаю, это дает мне идеи, что можно попробовать. Я пытаюсь применить одну из трех схем (Agent, AnonGroup или IdGroup). - person dmcquay; 02.05.2019