在JSON Schema中检查不同嵌套属性

3
有没有一种方法在嵌套的JSON Schema中实现与Postgres CHECK约束等效的功能?假设我们有两个属性的数据,每个属性都有嵌套属性。如何使第一个对象的必需内容取决于第二个对象?
我的真实案例是为GeoJSON对象构建JSON模式,该对象具有几何对象(即点或多边形,或null),以及“属性”对象中的其他属性。我希望根据几何类型更改所需的属性。
我尝试了以下两种解决方案,但均失败了:
  • 将“allOf”嵌套在“anyOf”中以涵盖所有可能性
  • 复制“definitions”,以具有没有几何形状的属性_no_geom、几何_no_geom、具有几何形状的属性_with_geom和几何_with_geom,并在“anyOf”中声明它们
由于属性/位置涵盖了几何的缺失,因此这将得到验证:
{
    "attributes": {
        "name": "Person2",
        "place": "City2"
    },
    "geometry": null
}

这也将被验证,因为使用几何图形时不再需要属性/位置: "最初的回答"
{
    "attributes": {
        "name": "Person1"
    },
    "geometry": {
        "type": "Point", 
        "coordinates": []
    }
}

编辑

在Relequestual的回答基础上,这是我得到的不令人满意的结果:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "geometryIsPoint": {
      "type": "object",
      "required": ["type"],
      "properties": {
        "type": {
          "const": "Point"
        }
      }
    },
    "partialAttributes": {
      "type": "object",
      "required": ["name"],
      "properties": {
        "name": {
          "type": "string"
        },
        "place": {
          "type": "string"
        }
      }
    },
    "fullAttributes": {
      "type": "object",
      "required": ["name", "place"],
      "properties": {
        "name": {
          "type": "string"
        },
        "place": {
          "type": "string"
        }
      }
    },
    "conditionalAttributes": {
      "allOf": [
        {
          "if": {
            "$ref": "#/definitions/geometryIsPoint"
          },
          "then": {
            "$ref": "#/definitions/partialAttributes"
          },
          "else": {
            "$ref": "#/definitions/fullAttributes"
          }
        }
      ]
    }
  },
  "properties": {
    "attributes": {
      "$ref": "#/definitions/conditionalAttributes"
    },
    "geometry": {
      "$ref": "#/definitions/geometryIsPoint"
    }
  }
}

如果移除attributes/place属性,此架构将无法验证以下内容。

最初的回答:

如果删除attributes/place属性,则此模式将无法验证以下内容。

{
    "attributes": {
        "name": "Person",
        "place": "INVALID IF THIS LINE IS REMOVED ;-("
    },
    "geometry": {
        "type": "Point", 
        "coordinates": {}
    }
}

您可以在“allOf”中使用“if/then/else”关键字来实现您在此处想要的内容。我希望今天能够为您提供更完整的答案。 - undefined
我假设你正在使用支持draft-7 JSON Schema的库。这个假设正确吗? - undefined
是的,我正在使用draft-7 JSON Schema。谢谢你的建议。我会尝试使用条件语句,但是根据文档的理解,条件块中的内容总是指向它们声明的属性,因此,在上面的示例中,我不能像这样做:如果geometry/type存在,则要求在attributes中有某些内容 - undefined
这是可能的。我很快会向你展示如何做到 =] - undefined
1个回答

1

您可以使用 if/then/else 关键字 条件地应用子模式。

我们只需要您的解决方案中有 ifthen

两者的值必须是 JSON Schema。

如果 if 的值导致正面断言(当将模式应用于实例时,它成功验证),则将实例应用于 then 的模式值。

这是该模式:

我已经在https://jsonschema.dev预先加载了模式和数据,这样你就可以实时测试它。
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "geometryIsPoint": {
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "const": "Point"
        }
      }
    },
    "geometryAsPoint": {
      "required": [
        "coordinates"
      ],
      "properties": {
        "coordinates": {
          "type": "array"
        }
      }
    },
    "geometry": {
      "allOf": [
        {
          "if": {
            "$ref": "#/definitions/geometryIsPoint"
          },
          "then": {
            "$ref": "#/definitions/geometryAsPoint"
          }
        }
      ]
    }
  },
  "properties": {
    "geometry": {
      "$ref": "#/definitions/geometry"
    }
  }
}

geometry属性引用了定义的geometry

allOf是一个包含模式的数组。

allOf[0].if的值引用了被定义为geometryIsPoint的模式。

定义为geometryIsPoint的模式应用于geometry值。如果验证成功,则应用所引用的then模式。

您不必使用引用来完成任何操作,但我认为这可以使意图更清晰。

根据需要扩展模式,为要识别的所有几何类型添加模式到allOf中。


编辑:

由于条件语句中的if未通过验证,您触发了其else条件。让我解释一下。

这是一个更新后的模式,以覆盖您修改后的用例。

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "geometry": {
      "type": "object",
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "enum": [
            "Point",
            "somethingelse",
            null
          ]
        }
      }
    },
    "geometryIsPoint": {
      "type": "object",
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "const": "Point"
        }
      }
    },
    "attributes": {
      "properties": {
        "name": {
          "type": "string"
        },
        "place": {
          "type": "string"
        }
      }
    },
    "partialAttributes": {
      "type": "object",
      "required": [
        "name"
      ]      
    },
    "fullAttributes": {
      "type": "object",
      "required": [
        "name",
        "place"
      ]
    },
    "conditionalAttributes": {
      "allOf": [
        {
          "if": {
            "required": [
              "geometry"
            ],
            "properties": {
              "geometry": {
                "$ref": "#/definitions/geometryIsPoint"
              }
            }
          },
          "then": {
            "required": [
              "attributes"
            ],
            "properties": {
              "attributes": {
                "$ref": "#/definitions/partialAttributes"
              }
            }
          },
          "else": {
            "required": [
              "attributes"
            ],
            "properties": {
              "attributes": {
                "$ref": "#/definitions/fullAttributes"
              }
            }
          }
        }
      ]
    }
  },
  "properties": {
    "attributes": {
      "$ref": "#/definitions/attributes"
    },
    "geometry": {
      "$ref": "#/definitions/geometry"
    }
  },
  "allOf": [
    {
      "$ref": "#/definitions/conditionalAttributes"
    }
  ]
}

这里有一个JSON Schema开发链接,您可以测试它。

我们在这里做的是分离关注点。
“attributes”和“geometry”的“形状”在相应键的定义中定义。这些模式不断言这些对象需要哪些键,只是如果提供了这些键,则必须是什么。
因为模式中的“$ref”使得模式中的所有其他关键字被忽略(对于草案-7或以下版本),在根级别,我已经将对“conditionalAttributes”的引用包装在一个“allOf”中。
“conditionalAttributes”是一个定义好的JSON模式。我使用了“allOf”,所以您可以添加更多的条件检查。
“conditionalAttributes.allOf [0].if”的值是一个JSON模式,并应用于您的JSON实例的根。它需要一个“geometry”的键和一个值为“geometryIsPoint”。(如果省略了“required”,则会出现验证问题,因为省略该键将通过if条件)。
当实例结果为true(验证有效)时,将在根级别应用then值模式。由于它是应用在根级别的,如果你想检查嵌套属性的值,你必须像在模式的根级别一样使用properties。这就是如何在实例的不同深度上执行条件模式应用(if/then/else)。你可以通过将模式值之一更改为false并查看错误来测试条件解析。请记住,truefalse都是有效的JSON模式,因此你可以编写"then": false以在期望应用then模式时(例如,if模式断言验证OK),导致错误。

这是一个非常好的示例,展示了如何使用if/then关键词。可能会将其复制到网站上。 - undefined
感谢您对JSON Schema的非常有用的输入和工作!不幸的是,我尚未成功地使其在引用几何属性之外的属性时运作,也就是应修改属性/位置要求的几何/类型属性。 - undefined
不客气。谢谢你的更新。最好的办法是你更新一下问题,展示新的需求。我也可以帮你解决这个问题。 - undefined
更新了。有很多内容,包括条件适用性的工作原理。如果你有任何进一步的问题或评论,请告诉我 =] - undefined
太棒了,这个可行并且回答了我的初步顾虑!还有很长的路要走,但是条件要求已经不是问题了 :) - undefined
很高兴能帮到你!如果你想请我喝杯咖啡,可以在我的个人资料中找到链接 =] - undefined

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