【问题标题】:Check differently nested properties in JSON Schema检查 JSON Schema 中不同的嵌套属性
【发布时间】:2019-11-25 01:03:11
【问题描述】:

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

我的真实案例场景是为 GeoJSON 对象构建 JSON 模式,该对象具有几何对象(即点或多边形,或 null)以及“属性”对象中的其他属性。我想根据几何类型更改所需的属性。

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

  • 在“anyOf”中嵌套“allOf”以涵盖所有可能性
  • 复制“定义”以获得 attributes_no_geom、geometry_no_geom、attribute_with_geom 和 geometry_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": {
        "name": "Person",
        "place": "INVALID IF THIS LINE IS REMOVED ;-("
    },
    "geometry": {
        "type": "Point", 
        "coordinates": {}
    }
}

【问题讨论】:

  • 您可以使用 if/then/else 关键字,包裹在 allOf 中来实现您想要的。我希望在今天的某个时候为您提供更全面的答案。
  • 我假设您正在使用支持 Draft-7 JSON 模式的库。对吗?
  • 是的,我使用的是 Draft-7 JSON 模式。感谢您的建议。我将尝试条件语句,但我对文档的理解是条件块的内容总是引用它们声明的属性,因此,在上面的示例中,我不能做类似if geometry/type then require something in attributes?
  • 有可能。我会告诉你多久=]

标签: json jsonschema json-schema-validator


【解决方案1】:

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

我们只希望 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,用于您想要识别的任意数量的几何类型。


编辑:

您遇到了条件的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 的“形状”在定义中使用相应的键进行定义。这些模式不会断言这些对象中需要哪些键,只断言它们必须是如果提供的话。

因为架构中的$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 模式断言验证 OK),则可以编写 "then": false 以导致错误。

【讨论】:

  • 这是一个很好的例子,说明如何使用if/then 关键字。最终可能会将其复制到网站上。
  • 感谢您为 JSON Schema 提供的非常有用的输入和工作!不幸的是,在引用几何属性之外的属性时,我还没有设法使其工作,即应该修改属性/位置要求的几何/类型属性
  • 不客气。感谢您的更新。最好更新您的问题以显示新要求。我也可以帮你解决这个问题。
  • 已更新。那里有很多,包括条件适用性如何工作。如果您有任何其他问题或 cmets =],请告诉我
  • 太棒了,这可以解决我最初的担忧!还有很长的路要走,但条件要求不再是问题:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-13
  • 2019-08-20
  • 1970-01-01
  • 2020-06-18
  • 2021-07-28
相关资源
最近更新 更多