有什么方法可以在 JSON 对象数组的架构中定义作用域机制吗?

Is there any way to define a scoping mechanism in JSON Schema for Arrays of Objects?

我想使用 JSON 架构来验证作为对象数组存在的数据。在这个用例中,我有一个人员列表,我想确保他们拥有某些属性,但这些属性并不详尽。

例如,如果我们有一个名叫 Bob 的人,我想确保 Bob 的身高、种族和位置设置为特定值。但我不太关心 Bob 的其他属性,如爱好、体重、关系状态。

有一个警告,那就是可以有多个 Bob,所以我不想检查所有 Bob。恰好每个人都有一个唯一的ID,我想通过指定的ID检查一个人的属性。

这里是所有存在的人的例子:

{
  "people": [
    {
      "name": "Bob",
      "id": "ei75dO",
      "age": "36",
      "height": "68",
      "ethnicity": "american",
      "location": "san francisco",
      "weight": "174",
      "relationshipStatus": "married",
      "hobbies": ["camping", "traveling"]
    },
    {
      "name": "Leslie",
      "id": "UMZMA2",
      "age": "32",
      "height": "65",
      "ethnicity": "american",
      "location": "pawnee",
      "weight": "139",
      "relationshipStatus": "married",
      "hobbies": ["politics", "parks"]
    },
    {
      "name": "Kapil",
      "id": "HkfmKh",
      "age": "27",
      "height": "71",
      "ethnicity": "indian",
      "location": "mumbai",
      "weight": "166",
      "relationshipStatus": "single",
      "hobbies": ["tech", "games"]
    },
    {
      "name": "Arnaud",
      "id": "xSiIDj",
      "age": "42",
      "height": "70",
      "ethnicity": "french",
      "location": "paris",
      "weight": "183",
      "relationshipStatus": "married",
      "hobbies": ["cooking", "reading"]
    },
    {
        "name": "Kapil",
        "id": "fDnweF",
        "age": "38",
        "height": "67",
        "ethnicity": "indian",
        "location": "new delhi",
        "weight": "159",
        "relationshipStatus": "married",
        "hobbies": ["tech", "television"]
      },
    {
      "name": "Gary",
      "id": "ZX43NI",
      "age": "29",
      "height": "69",
      "ethnicity": "british",
      "location": "london",
      "weight": "172",
      "relationshipStatus": "single",
      "hobbies": ["parkour", "guns"]
    },
    {
      "name": "Jim",
      "id": "uLqbVe",
      "age": "26",
      "height": "72",
      "ethnicity": "american",
      "location": "scranton",
      "weight": "179",
      "relationshipStatus": "single",
      "hobbies": ["parkour", "guns"]
    }
  ]
}

这是我特别想检查每个人的东西:

{
  "$schema": "https://json-schema.org/draft/2019-09/schema",
  "type": "object",
  "properties": {
    "people": {
      "type": "array",
      "contains": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "const": "ei75dO"
              },
              "name": {
                "const": "Bob"
              },
              "ethnicity": {
                "const": "american"
              },
              "location": {
                "const": "los angeles"
              },
              "height": {
                "const": "68"
              }
            },
            "required": ["id", "name", "ethnicity", "location", "height"]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "const": "fDnweF"
              },
              "name": {
                "const": "Kapil"
              },
              "location": {
                "const": "goa"
              },
              "height": {
                "const": "65"
              }
            },
            "required": ["id", "name", "location", "height"]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "const": "xSiIDj"
              },
              "name": {
                "const": "Arnaud"
              },
              "location": {
                "const": "paris"
              },
              "relationshipStatus": {
                "const": "single"
              }
            },
            "required": ["id", "name", "location", "relationshipStatus"]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "const": "uLqbVe"
              },
              "relationshipStatus": {
                "const": "married"
              }
            },
            "required": ["id", "relationshipStatus"]
          }
        ]
      }
    }
  },
  "required": ["people"]
}

请注意,对于 Bob,我只想检查他在记录中的名字是 Bob,他的种族是美国人,并且他的位置和身高设置正确。

对于 Kapil,请注意记录中有 2 个。我只想验证与 ID 为 fDnweF.

的 Kapil 相关的数组对象

对于 Jim,我只想确保他的 relationshipStatus 设置为 married

所以我的问题是,JSON Schema 中有没有什么方法可以说嘿,当你遇到对象数组而不是对数据中的每个元素进行 运行ning 验证时,仅 运行 它针对匹配特定标识符的对象。在我们的例子中,我们会说标识符是 id。你可以想象这个标识符可以是任何东西,例如,如果名单上的人都来自美国,它可能是 socialSecurity#

当前模式的问题是,当它尝试验证对象时,它会生成一个巨大的错误列表,没有明确指示哪个对象因哪个值失败。

在理想情况下,AJV(我目前使用的)会生成如下所示的错误:

---------Bob-------------
path: people[0].location
expected: "los angeles"

// Notice how this isn't Kapil at index 2 since we provided the id which matches kapil at index 4
---------Kapil-----------
path: people[4].location
expected: "goa"

---------Kapil-----------
path: people[4].height
expected: "65"

---------Arnaud----------
path: people[3].relationshipStatus
expected: "single"

-----------Jim-----------
path: people[6].relationshipStatus
expected: "married"

相反,目前 AJV 吐出我们的错误,但没有明确指出可能出现故障的位置。如果 bob 未能匹配 location 的期望值,则表示包括 bob 在内的每个人都有一个无效的位置,从我们的角度来看这是不正确的。

我如何定义一个可以解决这个用例的模式,我们可以使用 JSON 模式来查明我们数据中的哪些元素不符合我们的模式规定的内容。所有这些都是为了我们可以干净地存储这些模式错误以用于报告目的,并返回这些报告以准确查看哪些人(由数组的索引值表示)失败了哪些值。

编辑: 假设我们也想检查 Bob 的亲戚。例如,我们想创建一个模式来检查他们的具有给定 ID 的亲戚 ALSO 是否设置为位置:“洛杉矶”和另一个“橙县”。

{
    "people": [{
        "name": "Bob",
        "id": "ei75d0",
        "relationshipStatus": "married",
        "height": "68",
        "relatives": [
            {
                "name": "Tony",
                "id": "UDX5A6",
                "location": "los angeles",
              },
              {
                "name": "Lisa",
                "id": "WCX4AG",
                "location": "orange county",
              }
        ]
    }]
}

那么我的问题是,if/then/else 也可以应用于嵌套元素吗?我没有成功,但我会继续努力让它发挥作用,post 在这里更新 if/once 我会。

How can I define a schema that can resolve this use-case and we can use JSON Schema to pinpoint which elements in our data aren't in compliance with what our schema states

这有点麻烦,但我已经从“这不可能”变成了“你差不多可以做到”。

如果您re-structure您的架构如下...

{
  "$schema": "https://json-schema.org/draft/2019-09/schema",
  "type": "object",
  "properties": {
    "people": {
      "type": "array",
      "items": {
        "allOf":[
          {
            "if": {
              "properties": {
                "id": {
                  "const": "uLqbVe"
                }
              }
            },
            "then": {
              "type": "object",
              "properties": {
                "id": {
                  "const": "uLqbVe"
                },
                "relationshipStatus": {
                  "const": "married"
                }
              },
              "required": ["id", "relationshipStatus"]
            },
            "else": true
          }
        ]
      }
    }
  },
  "required": ["people"]
}

我们在这里做的是,对于数组中的每一项,如果对象具有特定的ID,则进行其他验证,否则有效。

它包裹在一个 allOf 中,因此您可以多次执行相同的模式。

需要注意的是,如果您没有包含所有 ID,或者如果您没有仔细检查您的架构,您将被告知一切都是有效的。

理想情况下,您应该另外检查您期望的 ID 是否确实存在。 (在同一架构中这样做很好。)

如果您在 https://jsonschema.dev 上通过删除 $schema 属性 对其进行测试,您会发现它大部分工作正常。 (这个 playground 只是 draft-07,但是 none 您使用的关键字无论如何都需要高于 draft-07 的任何内容。)

您可以在 https://json-everything.net/json-schema 上测试这个工作,然后给您完整的验证响应。

默认情况下,AJV 不会为您提供所有验证结果。有一个选项可以启用它,但我现在无法亲自测试结果。