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

时间:2019-07-16 10:04:14

标签: json jsonschema json-schema-validator

是否有一种方法可以在嵌套JSON模式中实现与CHECK约束等效的Postgres?假设我们有两个属性的数据,每个属性都有嵌套的属性。 JSON Schema如何使第一个对象的必需内容取决于第二个对象?

我的实际情况是为GeoJSON对象构建一个JSON模式,该对象具有一个几何对象(即Point或Polygon或null)以及“ properties”对象中的其他属性。我想根据几何类型更改所需的属性。

我无法通过以下两种解决方案:

  • 在“ anyOf”中嵌套“ allOf”以涵盖所有可能性
  • 复制“定义”以具有attribute_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": {}
    }
}

1 个答案:

答案 0 :(得分:0)

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

我们只希望ifthen为您提供解决方案。

两者的值都必须是JSON模式。

如果if的值导致一个肯定的断言(将模式应用于实例并成功验证时),则将then的模式值应用于实例。 / p>

这是架构。

我已经在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会忽略架构中的所有其他关键字(对于草稿7或更低版​​本),因此在根级别,我将对conditionalAttributes的引用包装在{{ 1}}。

allOf是已定义的JSON模式。我使用过conditionalAttributes,因此您可以添加更多条件检查。

allOf的值是一个JSON模式,并应用于JSON实例的根。它需要密钥conditionalAttributes.allOf[0].if,并且值为geometry。 (如果省略geometryIsPoint,则会遇到验证问题,因为省略该键将通过if条件。)

当实例为required值架构产生true断言(验证有效)时,则在根级别应用if值架构。

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

您可以通过将模式值之一更改为if/then/else并查看错误来测试条件解析。请记住,falsetrue是有效的JSON模式,因此,如果您希望应用false模式(例如,{ {1}}模式断言验证成功)。