为什么我的 JSON 架构似乎没有正确验证子对象?

Why does my JSON Schema appear to not validate a child object correctly?

鉴于下面的 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})+))$"
    }
  }
}

if/then/else 没有达到您的预期。

在您的模式定义中 allOfAgentGroup 有一个 oneOf 部分。 让我们单独看一下。

在您应该失败的示例数据中,让我们也单独使用 "actor" 对象。

架构:

{
  "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 Schema",总是导致该分支上的验证失败)。

现在,鉴于上述架构和实例,您会期望 JSON 实例数据无法通过验证,对吗? 它没有,实际上验证通过了。

请记住,每个 JSON 架构关键字都会为您的验证要求添加约束。 让我们看看 ifthen 实际上做了什么。

if

This validation outcome of this keyword's subschema has no direct effect on the overall validation result. Rather, it controls which of the "then" or "else" keywords are evaluated.

Instances that successfully validate against this keyword's subschema MUST also be valid against the subschema value of the "then" keyword, if present.

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

好的,如果 if 的架构成功验证,则会应用 then 的架构。

实例数据未通过 if 条件验证,因此...未应用来自 then 的模式,最终得到的是有效的 "empty schema",这意味着没有验证oneOf/0 的限制条件。这导致您 oneOf 断言验证成功,因为 oneOf/0 通过而 oneOf/1 失败。

要解决此问题,您需要将 "then": false 添加到包含 ifthen 的对象中,如果 [= =17=]条件不满足。