JSON 架构 oneof 没有要求

JSON Schema oneof without required

JSON Schema 草案 7 或草案 8 中,正确的说法是我们想要可选的属性之一而不是两者。我可以用 required 关键字构造,但是你必须包含其中一个必需的属性。这是我能想到的,对吗?

在这个例子中,我试图验证 field_1 是必需的,我们想要其中的一个 field_2field_3 或 none。 (查看示例)

{
  "$schema": "http://json-schema.org/draft-08/schema#",
  "type": "object",
  "properties": {
    "field_1": {
      "type": "string"
    },
    "field_2": {
      "type": "string"
    },
    "field_3": {
      "type": "string"
    }
  },
  "required": [
    "field_1"
  ],
  "oneof": [
    {
      "properties": [
        "field_2"
      ]
    },
    {
      "properties": [
        "field_3"
      ]
    }
  ],
  "additionalProperties": false,
  "examples": [
    {
      "field_1": "1",
      "field_2": "2"
    },
    {
      "field_1": "1",
      "field_3": "3"
    },
    {
      "field_1": "1"
    }
  ]
}

[已编辑 - 请参阅下面的第三个解决方案]

解决方案 1 - 至少一个,但不是两个(详细)

我会添加一个带有嵌套 oneOfallOf:

{
  ...
  "allOf": [
    {
      "oneOf": [
        {"required": ["field_2"]},
        {"required": ["field_3"]}
      ],
    },
    {
      "not": {
        "required": ["field_2", "field_3"]
      }
    }
  ],
  ...
}

这需要两个字段之一存在,但不能同时存在。


解决方案 2 - 至少一个,但不是两个

我认为这可以通过仅使用 oneOf:

来进一步简化
{
  ...
  "oneOf": [
    {"required": ["field_2"]},
    {"required": ["field_3"]}
  ],
  ...
}
  • 如果两者都不存在,那么它们都会失败,oneOf 也会失败。
  • 如果有,则 oneOf 通过。
  • 如果两者都存在,那么这两个都会通过,而 oneOf 失败。

是的,这样更好。看看你拥有的东西,我认为这就是你想要做的,所以你 真的 接近了!


解决方案 3 - 最多一个

要在两个字段都不存在时通过,您需要第一个解决方案,但将 allOf 更改为 anyOf:

{
  ...
  "anyOf": [
    {
      "oneOf": [
        {"required": ["field_2"]},
        {"required": ["field_3"]}
      ],
    },
    {
      "not": {
        "required": ["field_2", "field_3"]
      }
    }
  ],
  ...
}
  • 如果两者都不存在,则 not 子模式将通过,并且 anyOf 通过。
  • 如果存在,则 oneOf 子模式将通过,anyOf 将通过。
  • 如果两者都存在,则
    • 两个 oneOf 子模式都将通过,因此 oneOf 失败
    • not 子模式将失败
    • 所以 anyOf 失败了

您可以测试这些解决方案here

这里还有几个选项可以添加到@gregdennis 的回答中。

依赖关系

这是 dependencies 关键字的一个很好的用例。这表示,"if there is a "field_2" 不能有 "field_3" 并且,"if there is a "field_3" 不能有 "field_2"。如果 "field_2" 或 "field_3" 都不存在,则不应用任何约束并且模式通过。

"dependencies": {
  "field_2": { "not": { "required": ["field_3"] } },
  "field_3": { "not": { "required": ["field_2"] } }
}

最大属性

这个有点老套,但它的好处是可以单行。如果需要 "field_1" 并且只允许 "field_2" 或 "field_2" 之一,则此对象中的属性不能超过两个。当心,这个解决方案可能比它的价值更聪明。由于此约束间接起作用,因此将来可能会导致维护困难。

"maxProperties": 2

if/then

这只是 dependencies 选项的更详细版本,但对于阅读架构的人来说可能更具描述性。

"allOf": [
  {
    "if": { "required": ["field_2"] },
    "then": { "not": { "required": ["field_3"] } }
  },
  {
    "if": { "required": ["field_3"] },
    "then": { "not": { "required": ["field_2"] } }
  }
]

我不得不尝试解决 3 个字段的问题,其中字段 1 也是可选的。这样做揭示了@gregdennis 对 解决方案 3 的回答略多于它需要的,这实现了相同的目标,因为不需要检查所需的单个值,因为两者都不需要。

{
  ...
  "not": {
    "required": ["field_2", "field_3"]
  } 
  ...
}

要为 3 执行此操作,它看起来像:

{
  ...
  "not": {
    "anyOf": [{
      "required": ["field_1", "field_2", "field_3"]
    },
    {
      "required": ["field_1", "field_2"]
    },
    {
      "required": ["field_2", "field_3"]
    },
    {
      "required": ["field_1", "field_3"]
    }]
  }
  ...
}

随着您添加更多字段,这显然变得不那么有用了。因此,有空间去 N 的更好解决方案如下:

{
  ...
  "anyOf": [
    {
      "oneOf": [
        {"required": ["field_1"]},
        {"required": ["field_2"]},
        {"required": ["field_3"]}
      ]
    },
    {
      "not": {
        "anyOf": [
          {"required": ["field_1"]},
          {"required": ["field_2"]},
          {"required": ["field_3"]}
        ]
      }
    }
  ]
  ...
}

你可以玩它here