简体   繁体   English

检查JSON模式中不同嵌套的属性

[英]Check differently nested properties in JSON Schema

Is there a way to implement the Postgres equivalent to CHECK constraint within a nested JSON Schema? 有没有一种方法可以在嵌套JSON模式中实现与CHECK约束等效的Postgres? Say we have data that has two properties, each of which has nested properties. 假设我们有两个属性的数据,每个属性都有嵌套的属性。 How can JSON Schema make the required contents of the first object depend on the second? JSON Schema如何使第一个对象的必需内容取决于第二个对象?

My real case scenario is to build a JSON schema for a GeoJSON objects, that has a geometry object (ie Point or Polygon, or null), and other attributes in a "properties" object. 我的实际情况是为具有几何对象(即Point或Polygon或null)以及“ properties”对象中的其他属性的GeoJSON对象构建JSON模式。 I want to alter the required properties depending on the type of geometry. 我想根据几何类型更改所需的属性。

I failed with both the following solutions: 我无法通过以下两种解决方案:

  • Nest "allOf" inside "anyOf" to cover all the possibilities 将“ allOf”嵌套在“ anyOf”内部以涵盖所有可能性
  • Duplicate the "definitions" to have a attributes_no_geom, geometry_no_geom, attribute_with_geom and geometry_with_geom and declare them in a "anyOf" 复制“定义”以具有attribute_no_geom,geometry_no_geom,attribute_with_geom和geometry_with_geom并在“ anyOf”中声明它们

This would validate since attribute/place covers for the lack of geometry: 这将验证,因为属性/位置涵盖了缺少几何图形的情况:

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

This would also validate since attribute/place is no longer required with a geometry: 这也将验证,因为几何不再需要属性/位置:

{
    "attributes": {
        "name": "Person1"
    },
    "geometry": {
        "type": "Point", 
        "coordinates": []
    }
}

EDIT 编辑

Building on Relequestual's answer, this is the unsatisfactory result I'm getting : 基于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"
    }
  }
}

This schema will not validate the following if the attributes/place property is removed. 如果删除了attributes/place属性,则该架构将无法验证以下内容。

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

You can use if/then/else keywords to apply subschemas conditionally. 您可以使用if/then/else关键字有条件地应用子计划。

We only want if and then for your solution. 我们只希望ifthen您的解决方案。

The value of both must be a JSON Schema. 两者的值都必须是JSON模式。

If the value of if results in a positive assertion (when the schema is applied to the instance, and it validates successfully), then the schema value of then is applied to the instance. 如果该值if得到正断言(当模式被应用到该实例,并成功验证),那么的模式值then应用到该实例。

Here's the schema. 这是架构。

I've pre-loaded the schema and data at https://jsonschema.dev so you can test it live. 我已经在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"
    }
  }
}

The property geometry references the definition geometry . 属性geometry引用定义geometry

allOf is an array of schemas. allOf是一个模式数组。

The value of allOf[0].if references the schema defined as geometryIsPoint . allOf[0].if的值引用定义为geometryIsPoint的架构。

The schema defined as geometryIsPoint is applied to the geometry value. 定义为geometryIsPoint的架构将应用于该geometry值。 If it validates successfully, then the then referenced schema is applied. 如果验证成功,则将应用then引用的架构。

You don't have to use referencing to do any of this, but I feel it makes the intent clearer. 您不必使用引用来执行任何上述操作,但是我认为这样做可以使意图更清晰。

Extend the schema as required, adding schemas to allOf for as many geometry types as you want to recognise. 根据需要扩展模式,将模式添加到allOf以便包含您要识别的多个几何类型。


Edit: 编辑:

You were hitting the else condition of your conditional, because the if failed validation. 您要达到条件的else条件,因为if验证失败。 Let me explain. 让我解释。

Here's an updated schema to cover your modified use case. 这是一个更新的架构,涵盖了您修改后的用例。

{
  "$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"
    }
  ]
}

Here's a JSON Schema dev link so you can test it. 这是JSON Schema开发人员链接,因此您可以对其进行测试。

What we're doing here is splitting up the concerns. 我们在这里所做的是分散关注点。

The "shape" of attributes and geometry is defined in definitions with the corresponding key. attributesgeometry的“形状”是在定义中使用相应的键定义的。 Those schemas do not assert which keys are required in those objects, only what they must be if provided. 这些模式没有断言那些对象中需要哪些键,只有提供它们时才必须断言。

Because $ref in a schema makes all other keywords in a schema ignored (for draft-7 or below), at the root level, I've wrapped the reference to conditionalAttributes in an allOf . 由于架构中的$ref会忽略架构中的所有其他关键字(对于draft-7或更低版​​本),因此在根级别,我将对conditionalAttributes的引用包装在allOf

conditionalAttributes is a defined JSON Schema. conditionalAttributes是已定义的JSON模式。 I've used allOf so you can add more conditional checks. 我使用过allOf因此您可以添加更多条件检查。

The value of conditionalAttributes.allOf[0].if is a JSON Schema, and is applied to the root of your JSON instance. conditionalAttributes.allOf[0].if的值是一个JSON模式,并应用于您的JSON实例的根。 It requires a key of geometry and that the value is geometryIsPoint . 它需要一个geometry键,并且值是geometryIsPoint (If you omit the required , you'll end up with validation issues, because omitting that key will then pass the if condition). (如果省略了required ,则会遇到验证问题,因为省略该键将通过if条件)。

When the instance results in a true assertion (validation valid) for the if value schema, then the then value schema is applied at the root level. 当实例为if值架构得出true断言(验证有效)时,则在根级别应用then值架构。

Because it's applied at the root level and you want to check the value of a nested property, you have to use properties as you would if you were at the root level of your schema. 因为它是在根级别应用的,并且您要检查嵌套属性的值,所以您必须像在架构的根级别一样使用properties THIS is how you do conditional schema application ( if/then/else ) across different depths of your instance. 是您在实例的不同深度进行条件模式应用程序( if/then/else )的方式。

You can test out the conditional resolution by changing one of the schema values to false and looking at the errors. 您可以通过将模式值之一更改为false并查看错误来测试条件解析。 Remember, true and false are valid JSON Schemas, so you can write "then": false to cause an error if you expect the then schema to be applied (as in, the if schema asserted validation OK). 请记住, truefalse是有效的JSON模式,因此,如果您希望应用then模式, "then": false可以编写"then": false导致错误(例如, if模式断言验证正常)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM