JSON 可以包含具有不同键的项的数组的模式验证

JSON schema validation for array that can have items with different keys

我试图找到一个模式来验证具有多个项目的给定数组。这些项目可以有 2 个可能的键值。但是所有项目都应该与键具有相同的值。

如果 2 个可能的值是 'primary' 和 'secondary',那么所有键都应该是 'primary' 或者所有键都应该是 'secondary'。 oneOf 在这种情况下似乎不起作用。

有解决办法吗?任何帮助表示赞赏。谢谢。

架构:

{
  type: "object",
  properties: {
    values: {
      type: "array",
      uniqueItems: true,
      minItems: 1,
      maxItems: 100,
      items: { 
        anyOf: [  
          { $ref: "#/definitions/primaryObj"} ,
          { $ref: "#/definitions/secondaryObj"} 
        ]
      }
    },
  },
  definitions: {
    primaryObj: {
      type: "object",
      required: ["id", "primary"],
      properties: {
        id: {
          type: "string",
          description: "The id",
        },
        primary: {
          type: "string",
          description: "primary value",
        },
      },
    },
    secondaryObj: {
      type: "object",
      required: ["id", "secondary"],
      properties: {
        id: {
          type: "string",
          description: "The id",
        },
        secondary: {
          type: "string",
          description: "secondary value",
        },
      },
    },
  },
  required: ["values"],
}

示例输入 -

输入 1 - 应通过验证

{
    "values": [
        {
            "id": "1",
            "primary" : "hello"
        },
        {
            "id": "2",
            "primary" : "world"
        }
    ]
}

输入 2 - 应通过验证

{
    "values": [
        {
            "id": "1",
            "secondary" : "hello"
        },
        {
            "id": "2",
            "secondary" : "world"
        }
    ]
}

输入 3 - 验证失败

{
    "values": [
        {
            "id": "1",
            "primary" : "hello"
        },
        {
            "id": "2",
            "secondary" : "world"
        }
    ]
}

你离这里很近。您需要进行两项更改才能获得所需的验证。 (我假设您使用的是 draft-07,尽管这也适用于较新的草稿)

首先,让我们来看看最上面的部分。

anyOf关键字指定如下:

An instance validates successfully against this keyword if it
validates successfully against at least one schema defined by this
keyword's value.

https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.7.2

您只希望引用的子模式之一为真!

oneOf定义类似:

An instance validates successfully against this keyword if it
validates successfully against exactly one schema defined by this
keyword's value.

https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.7.3

因此我们更改您的架构以检查是否只有一个引用有效...

      "items": {
        "oneOf": [
          {
            "$ref": "#/definitions/primaryObj"
          },
          {
            "$ref": "#/definitions/secondaryObj"
          }
        ]
      }

但这仍然是不正确的。让我们刷新一下 items 的作用。

This keyword determines how child instances validate for arrays, and does not directly validate the immediate instance itself.

If "items" is a schema, validation succeeds if all elements in the array successfully validate against that schema.

https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.4.1

看起来我们做对了,但是上面引用的第一段试图传达 items 将其子模式值应用于数组中的每个项目,而不是“作为整个数组”。

我们上面的子模式正在做的是单独检查数组中的每个项目,与其他项目隔离,它们是您定义的“主要”或“次要”。

我们想要做的是检查数组中的所有项目是“主要”还是“次要”。为此,我们需要将 oneOf 移到 items.

之外
      "oneOf": [
        {
          "items": {
            "$ref": "#/definitions/primaryObj"
          }
        },
        {
          "items": {
            "$ref": "#/definitions/secondaryObj"
          }
        }
      ]

快到了!这几乎可行,但我们仍然发现混合主要和次要不会导致验证失败。

让我们检查一下我们的假设。我们假设当实例数据在数组中的对象中有 primarysecondary 时验证应该失败。我们可以通过将 oneOf 中的一个子模式更改为 false 来测试这一点,强制检查第一个子模式定义(主要)。它应该检查数组中的所有项目都是主要的,任何次要的都应该导致验证失败。

我们必须记住,JSON 架构是基于约束的。任何不受约束的东西都是允许的。

如果我们查看 primaryObj 的定义,它需要并定义 idprimary, but this doesn't inherintly prevent additioanl keys in the object. To do that, we need to add "additionalProperties": false`(对于两个定义)的验证。

最终结果如下所示。您可以在 https://jsonschema.dev/s/3ZKBp

查看现场演示
{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "properties": {
    "values": {
      "type": "array",
      "uniqueItems": true,
      "minItems": 1,
      "maxItems": 100,
      "oneOf": [
        {
          "items": {
            "$ref": "#/definitions/primaryObj"
          }
        },
        {
          "items": {
            "$ref": "#/definitions/secondaryObj"
          }
        }
      ]
    }
  },
  "definitions": {
    "primaryObj": {
      "type": "object",
      "required": [
        "id",
        "primary"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "The id"
        },
        "primary": {
          "type": "string",
          "description": "primary value"
        }
      },
      "additionalProperties": false
    },
    "secondaryObj": {
      "type": "object",
      "required": [
        "id",
        "secondary"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "The id"
        },
        "secondary": {
          "type": "string",
          "description": "secondary value"
        }
      },
      "additionalProperties": false
    }
  },
  "required": [
    "values"
  ]
}