首页 > 解决方案 > 如何根据 JSON Schema 中其他属性的存在有条件地禁止属性?

问题描述

在我的模式中,我声明了这些属性:

"index_name": {
      "type": "string",
      "examples": ["foo-wwen-live", "foo"]
    },
"locale": {
      "type": "string",
      "examples": ["wwen", "usen", "frfr"]
},
"environment": {
      "type": "string",
      "default": "live",
      "examples": [
        "staging",
        "edgengram",
        "test"
      ]
}

我希望针对我的架构验证的 JSON 正文仅在以下情况下有效:

简而言之,locale绝不environment应与index_name.

测试用例和期望的结果:

这些应该通过:
案例#1

{
  "locale": "usen"
}

案例#2

{
  "environment": "foo"
}

案例#3

{
  "environment": "foo",
  "locale": "usen"
}

案例#4

{
  "index_name": "foo-usen"
}

这些不应该通过:
案例#5

{
  "index_name": "foo-usen",
  "locale": "usen"
}

案例#6

{
  "index_name": "foo-usen",
  "environment": "foo"
}

案例#7

{
  "index_name": "foo-usen",
  "locale": "usen",
  "environment": "foo"
}

我为我的架构创建了以下规则,但它并不涵盖所有情况。例如,如果localeenvironment都存在,如果也存在,则验证返回失败index_name,根据案例 #7,这是正确的行为。但是如果只有一个localeandenvironment存在,它也允许index_name存在(在 #5 和 #6 的情况下失败)。

  "oneOf": [
    {
      "required": ["index_name"],
      "not": {"required":  ["locale", "environment"]}
    },
    {
      "anyOf": [
        {
          "required": ["locale"],
          "not": {"required": ["index_name"]}
        },
        {
          "required": ["environment"],
          "not": {"required": ["index_name"]}
        }
      ]
    }
  ]

我得到了关于"not": {"required": []}声明如何工作的混合信息。有些人声称这意味着它禁止在数组中声明的任何内容存在,这与语法给出的想法相反。其他人声称这应该完全按照听起来:数组中列出的属性不是必需的- 它们可以存在,但如果它们不存在也没关系。

除了这条规则,我还要求在所有情况下都存在一个不相关的属性,并且我设置了"additionalProperties": false.

满足我所有测试用例的规则是什么?

标签: jsonvalidationjsonschema

解决方案


依赖项

这是dependencies关键字的工作。下面说

  • 如果存在“locale”,则禁止使用“index_name”。
  • 如果存在“环境”,则禁止“索引名称”。

|

"dependencies": {
  "locale": { "not": { "required": ["index_name"] } },
  "environment": { "not": { "required": ["index_name"] } }
}

怎么了not- required

not有一个关于如何工作的子问题required。这很令人困惑,因为它并不意味着它在英语中的阅读方式,但它的相似性足以让我们有时认为它确实如此。

在上面的例子中,如果我们把它读作“不需要”,听起来它的意思是“可选的”。更准确的描述是“禁止”。

这很尴尬,但也不算太糟糕。当您想要“禁止”多个属性时,它会变得令人困惑。假设我们想说,如果“foo”存在,那么“bar”和“baz”是被禁止的。你可以尝试的第一件事就是这个。

"dependencies": {
  "foo": { "not": { "required": ["bar", "baz"] } }
}

但是,这说明如果“foo”存在,那么如果“bar”“baz”都存在,则实例无效。他们都必须在那里触发失败。我们真正想要的是,如果存在“bar”“baz”,它就无效。

"dependencies": {
  "foo": {
    "not": {
      "anyOf": [
        { "required": ["bar"] },
        { "required": ["baz"] }
      ]
    }
  }
}

为什么这么难?

JSON Schema 针对可容忍更改的模式进行了优化。模式应该强制实例具有完成特定任务所需的数据。如果它有超过它需要的,应用程序忽略其余的。这样,如果将某些内容添加到实例中,一切仍然有效。如果实例有一些应用程序不使用的额外字段,它不应该验证失败。

因此,当您尝试执行诸如禁止您原本可以忽略的事情之类的事情时,您会有点违背 JSON Schema 的原则,事情会变得有些难看。但是,有时它是必要的。我对您的情况了解不多,无法拨打电话,但我想dependencies在这种情况下这可能是必要的,但additionalProperties不是。


推荐阅读