JSON.net anyOf 模式验证问题

Issue with JSON.net anyOf schema validation

我在使用 JSON.Net schema validation package 时遇到了一个非常奇怪的问题。我已将问题追查到以下 anyObject 定义中 anyOf 的使用:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/schemas/example/1.0/schema.json",
  "anyOf": [
    {
      "$ref": "#/definitions/anyObject"
    }
  ],
  "definitions": {
    "anyObject": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ],
      "anyOf": [
        {
          "if": {
            "properties": {
              "type": {
                "const": "typeA"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/typeA"
          },
          "else": false
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "typeB"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/typeB"
          },
          "else": false
        }
      ]
    },
    "bodyDefinition": {
      "oneOf": [
        {
          "if": {
            "properties": {
              "$computed": {
                "type": "string"
              }
            },
            "required": [
              "$computed"
            ]
          },
          "then": {
            "$ref": "#/definitions/computedBody"
          },
          "else": {
            "$ref": "#/definitions/wildcardBody"
          }
        },
        {
            "type": "string"
        }
      ]
    },
    "wildcardBody": {
      "type": "object",
      "additionalProperties": {
        "$ref": "#/definitions/bodyDefinition"
      }
    },
    "firstComputedValue": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "$computed": {
          "const": "first"
        },
        "values": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/definitions/bodyDefinition"
          }
        }
      },
      "required": [
        "$computed",
        "values"
      ]
    },
    "computedBody": {
      "oneOf": [
        {
          "if": {
            "properties": {
              "$computed": {
                "const": "first"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/firstComputedValue"
          },
          "else": false
        }
      ]
    },
    "typeA": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "const": "typeA"
        },
        "body": {
          "$ref": "#/definitions/bodyDefinition"
        }
      },
      "required": [
        "type"
      ]
    },
    "typeB": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "const": "typeB"
        },
        "body": {
          "$ref": "#/definitions/bodyDefinition"
        }
      },
      "required": [
        "type"
      ]
    }
  }
}

当我测试这个时 json:

{
  "type": "typeB",
  "body":{
     "$computed":"first",
     "values":[]
  }
}

应该被标记为无效,因为values被要求至少有一个值。然而它是有效的。下面的 JSON 应该被认为是有效的,并且上面的模式确实断言正确:

{
  "type": "typeB",
  "body":{
     "$computed":"first",
     "values":["foo"]
  }
}

如果我从 anyObject 定义中删除 typeA,则验证会正确执行。以下是正确验证的模式:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/schemas/example/1.0/schema.json",
  "anyOf": [
    {
      "$ref": "#/definitions/anyObject"
    }
  ],
  "definitions": {
    "anyObject": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ],
      "anyOf": [
        {
          "if": {
            "properties": {
              "type": {
                "const": "typeB"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/typeB"
          },
          "else": false
        }
      ]
    },
    "bodyDefinition": {
      "oneOf": [
        {
          "if": {
            "properties": {
              "$computed": {
                "type": "string"
              }
            },
            "required": [
              "$computed"
            ]
          },
          "then": {
            "$ref": "#/definitions/computedBody"
          },
          "else": {
            "$ref": "#/definitions/wildcardBody"
          }
        },
        {
            "type": "string"
        }
      ]
    },
    "wildcardBody": {
      "type": "object",
      "additionalProperties": {
        "$ref": "#/definitions/bodyDefinition"
      }
    },
    "firstComputedValue": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "$computed": {
          "const": "first"
        },
        "values": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/definitions/bodyDefinition"
          }
        }
      },
      "required": [
        "$computed",
        "values"
      ]
    },
    "computedBody": {
      "oneOf": [
        {
          "if": {
            "properties": {
              "$computed": {
                "const": "first"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/firstComputedValue"
          },
          "else": false
        }
      ]
    },
    "typeA": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "const": "typeA"
        },
        "body": {
          "$ref": "#/definitions/bodyDefinition"
        }
      },
      "required": [
        "type"
      ]
    },
    "typeB": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "const": "typeB"
        },
        "body": {
          "$ref": "#/definitions/bodyDefinition"
        }
      },
      "required": [
        "type"
      ]
    }
  }
}

任何人都可以看看这个定义是否有问题,或者这是 JSON.Net 架构包的问题吗?

此测试是针对 https://www.jsonschemavalidator.net/

上的模式验证器的在线版本完成的

我认为这是一个错误。我会和图书馆作者谈谈!

为了调试,我通过架构遵循验证过程,将 $refthenelse 设置为 false... 当我达到 computedBody, 我改成了下面的...

"computedBody": {
      "if": {
        "properties": {
          "$computed": {
            "const": "first"
          }
        }
      },
      "then": false,
      "else": false
    }

验证仍然是肯定的,这应该是不可能的。我通过将该子模式设置为 false 并看到验证返回负值来证明它达到了 computedBody

(不需要 oneOf 包装 computedBody 的子模式。if 在模式级别有效。