为什么我使用的 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,最后一次出现,导致始终通过验证。交换 truefalse 以双重确认。

只要您在架构中的同一个对象中多次使用特定 *of 关键字(或在一个对象中多次使用任何键),您就需要修复它。