检查 JSON 架构中的不同嵌套属性

Check differently nested properties in JSON Schema

有没有办法在嵌套的 JSON 模式中实现 Postgres 等同于 CHECK 约束?假设我们的数据有两个属性,每个属性都有嵌套属性。 JSON Schema 如何使第一个对象的所需内容依赖于第二个对象?

我的真实案例场景是为 GeoJSON 对象构建一个 JSON 模式,它有一个几何对象(即点或多边形,或空),以及 "properties" 对象。我想根据几何类型更改所需的属性。

我在以下两种解决方案中都失败了:

这将得到验证,因为 attribute/place 弥补了几何图形的缺失:

{
    "attributes": {
        "name": "Person2",
        "place": "City2"
    },
    "geometry": null
}

这也将有效,因为几何不再需要 attribute/place:

{
    "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": {
        "name": "Person",
        "place": "INVALID IF THIS LINE IS REMOVED ;-("
    },
    "geometry": {
        "type": "Point", 
        "coordinates": {}
    }
}

您可以使用 if/then/else keywords 有条件地应用子模式。

我们只需要 ifthen 作为您的解决方案。

两者的值都必须是 JSON 架构。

如果 if 的值导致肯定断言(当模式应用于实例并成功验证时),则 then 的模式值应用于实例.

这是架构。

我 pre-loaded 架构和数据位于 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 以获得您想要识别的尽可能多的几何类型。


编辑:

您达到了条件的 else 条件,因为 if 验证失败。让我解释一下。

这是一个更新的架构,涵盖您修改后的用例。

{
  "$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 dev link 你可以测试一下。

我们在这里所做的是分散关注点。

attributesgeometry的“形状”在definitions中用相应的key定义。这些模式不会断言这些对象中需要哪些键,只有在提供时它们必须是什么。

因为模式中的 $ref 会忽略模式中的所有其他关键字(对于 draft-7 或更低版本),在根级别,我将对 conditionalAttributes 的引用包装在一个allOf.

conditionalAttributes 是定义的 JSON 架构。我使用了 allOf,因此您可以添加更多条件检查。

conditionalAttributes.allOf[0].if 的值是一个 JSON 架构,并应用于您的 JSON 实例的根。它需要 geometry 的键和 geometryIsPoint 的值。 (如果您省略 required,您最终会遇到验证问题,因为省略该键将通过 if 条件)。

当实例为 if 值模式生成 true 断言(验证有效)时,将在根级别应用 then 值模式。

因为它在根级别应用并且您想检查嵌套 属性 的值,所以您必须像在架构的根级别一样使用 properties . 是您如何跨实例的不同深度执行条件模式应用程序 (if/then/else)。

您可以通过将其中一个架构值更改为 false 并查看错误来测试条件解析。请记住,truefalse 是有效的 JSON 模式,因此如果您希望应用 then 模式(如在中,if 模式断言验证正常)。