如何在JSON Schema中模拟一个switch语句(switch-case)?

3
我正在使用 JSON Schema(jsonschema)来验证 JSON 记录。以下是一个示例模式。这里只有两种情况,但想象一下类似的场景,如果有一百个类似于这样的情况呈现在面前。
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "oneOf": [
      {
        "type": "object",
        "required": ["a", "b", "c"],
        "properties": {
          "a": {"type": "integer", "enum": [0]},
          "b": {"type": "integer", "enum": [0, 2, 4, 6, 8]},
          "c": {"type": "string", "enum": ["always the same"]}
        }
      },
      {
        "type": "object",
        "required": ["a", "b", "c"],
        "properties": {
          "a": {"type": "integer", "enum": [1]},
          "b": {"type": "integer", "enum": [1, 3, 5, 7, 9]},
          "c": {"type": "string", "enum": ["always the same"]}
        }
      }
    ]
}

关键问题是字段 "c" 的重复。我想能够在 "a" 上使用 switch-case,验证相应的 "b",但是保持 "c" 始终不变。我不想一百次拼写出 "c"。这个可能吗?
谢谢!
2个回答

6

是的,它可以完成。事实上,只在 anyOf / oneOf 中放入变化的部分是一个好的做法。

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "c": { "const": "always the same" }
  },
  "required": ["a", "b", "c"],
  "anyOf": [
    {
      "properties": {
        "a": { "const": 0 },
        "b": { "enum": [0, 2, 4, 6, 8] }
      }
    },
    {
      "properties": {
        "a": { "const": 1 },
        "b": { "enum": [1, 3, 5, 7, 9] }
      }
    }
  ]
}

1
在draft-7中,您还可以使用const而不是只有一个值的枚举。 - Relequestual
1
啊,是的,我整理了一些东西,但是我错过了那个。const在draft-06中被添加,供需要知道的人使用。 - Jason Desrosiers
我可以问一下为什么您会选择在这种情况下使用anyOf而不是oneOf吗?我可以看出anyOf可以正常工作,因为定义属性只有一个值,但似乎oneOf会更少出错,因为它要求每个子模式中的a的值都不同。 - Vorticity
@Vorticity anyOf 可以通过验证器更有效地进行评估。使用 anyOf,验证器可以在找到匹配项后停止评估 anyOf 模式(可以短路)。对于 oneOf,验证器必须检查 oneOf 中的每个模式,以确保只有一个模式与之匹配。在这种模式是相互排斥的情况下,oneOf 做了不必要的工作。 - Jason Desrosiers
无论如何,我不鼓励这种模式,而是更倾向于使用if/then/else,因为它更高效,并且会产生更好的错误消息。https://dev59.com/b1kT5IYBdhLWcg3wXuTP#38781027 - Jason Desrosiers

0

switch语句在JSON Schema中不存在。关于提议它的讨论很长,可以在这里找到。没有它,有几种可能的方法来提供这个功能:

anyOf / oneOf

Jason Desrosiers建议的解决方案可以完成任务,但有一些注意事项:

  1. 您只能通过手动构造其他可能值来创建“默认”情况。对于Jason答案中的示例,可能看起来像这样:
      "properties": {
        "a": { "not": { "enum": [0, 1] } },
        "b": { "enum": [10, 11, 12] }
      }
  • 如果使用oneOf,需要注意确保所有情况是相互独立的,不能重叠。
  • if...then

    个人而言,我更喜欢一组由if...then片段(由allOf包围)组成的方式。这与上述方法具有相同的注意事项。其中一个优点是它清楚地表明了正在“切换”的属性。另一个优点是,如果使用像ajv这样的验证器,当JSON未通过验证时接收到的验证消息将更好地指出问题所在。如果使用anyOfoneOf,您将仅收到整个内容未满足的验证消息,这使得原因不太清楚。但是使用if片段,您将被告知哪个条件未满足以及原因。以下是Jason的示例转换为此语法:

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "c": { "const": "always the same" }
      },
      "required": ["a", "b", "c"],
      "allOf": [
        {
          "if": {
            "properties": {
              "a": { "const": 0 },
            }
          },
          "then": {
            "properties": {
              "b": { "enum": [0, 2, 4, 6, 8] }
            }
          }
        },
        {
          "if": {
            "properties": {
              "a": { "const": 1 },
            }
          },
          "then": {
            "properties": {
              "b": { "enum": [1, 3, 5, 7, 9] }
            }
          }
        }
      ]
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接