【问题标题】:Validate each JSON node with different JSON schema使用不同的 JSON 模式验证每个 JSON 节点
【发布时间】:2023-04-01 20:02:01
【问题描述】:

我正在尝试制作一个系统监视器,它可以由用户高度定制。这种定制是通过使用 JSON 文件对系统监视器的外观进行建模来实现的。 JSON 可能如下所示。

{
  "_": "WINDOW",
  "name": "myWindow",
  "children": [
    {
      "_": "CPU",
      "name": "cpuMonitor",
      "freq_Unit": "MHZ"
    },
    {
      "_": "NETWORK",
      "name": "network",
      "unit": "Kb/s"
    },
    {
      "_": "DISK",
      "name": "disk"
    }
  ],
  "background": "red"
}

如您所见,每个对象都对应此架构。

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "name":"Component",
    "type": "object",
    "properties":{
        "_": {
            "type": "string"
        },
        "name":{
            "type":"string"
        },
        "childern":{
            "type":"array"
        }
    },

    "required": ["_","name"]
}

但每个组件也有自己的架构定义。我想解析整个 JSON 并为不同的模式验证每个节点(首先是它的组件,然后是它对应的模式)。

我查看了 rapidJson 和其他库,但我没有找到验证不同模式的节点的解决方案。你知道任何可以做到这一点的图书馆吗?或者甚至有可能以这种方式验证 JSON?

我们将不胜感激所有有关如何解决此问题的反馈。

编辑:更正架构:(

【问题讨论】:

  • 本题与C++无关。

标签: json jsonschema rapidjson


【解决方案1】:

有一个简单的方法,使用oneOf 模式声明来指定数组元素的布局。在这些嵌套声明中,您将固定标识符(可能是 _ 字段的内容)指定为常量,以便只有一个嵌套架构与您的每种面板类型匹配。

注意事项:

  • 我必须使用enum 说明符指定常量类型标识符,因为常规的constant 说明符不适用于我使用的库。这也可能是对它所依据的规范的修订中的疏忽。
  • 另一种方法是拆分验证步骤。您只需验证数组的元素是对象,并且它们有一个字符串字段_,其中包含一种受支持的类型。遍历数组时,您可以根据其 _ 字段单独验证每个字段。

【讨论】:

    【解决方案2】:

    除了Ulrich's answer,下面是我要做的一个例子:

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "Component",
      "type": "object",
      "definitions": {
        "base": {
          "properties": {
            "name": { "type": "string" },
            "children": {
              "type": "array",
              "items": { "$ref": "#" }
            }
          },
          "required": [ "_", "name" ]
        },
        "cpu": {
          "properties": {
            "_": { "const": "CPU" },
            "freq_Unit": "MHZ"
          }
        },
        "network": {
          "properties": {
            "_": { "const": "NETWORK" },
            "unit": "Kb/s"
          }
        },
        "disk": {
          "properties": {
            "_": { "const": "DISK" }
          }
        },
        "window": {
          "properties": {
            "_": { "const": "WINDOW" },
            "background": { "enum": [ "red", "orange", "yellow", ... ] }
          }
        }
      },
      "allOf": [
        { "$ref": "#/definitions/base" },
        {
          "oneOf": [
            { "$ref": "#/definitions/cpu" },
            { "$ref": "#/definitions/network" },
            { "$ref": "#/definitions/disk" },
            { "$ref": "#/definitions/window" }
          ]
        }
      ]
    }
    

    首先,我们要求任何实例必须遵守base,它将_name 声明为必需属性。此外,我们声明了一个 children 数组属性,要求所有项目也匹配此模式(给我们一个递归行为)。除了允许我们在一个地方声明这些东西而不必在其他三个定义中声明它们之外,这实际上并没有多大作用。

    (请注意,我们没有在属性列表中声明 _。这意味着 any 值将传递给架构的这一部分。我们将在下一部分清理它。如果您想确保未来的组件是用字符串声明的,那么您可以向该属性添加 "type": "string" 要求,但我认为除非其他人正在创作这些组件,否则没有必要。)

    其次,我们将每个特定类型声明为单独的定义,使用const 关键字来隔离我们想要的类型。此构造类似于switch(或case)语句。如果实例与这些显式选项之一不匹配,则会失败。如果它缺少所需的基本属性之一,则会失败。

    这会让你到达你想去的地方。

    更进一步,您还可以做两件事:

    1. required 添加到其他定义中,表示还需要特定属性(例如freq_Unit 用于cpu 定义)。
    2. 在单独的文件中声明每个定义。这将允许您通过简单地添加新文件并在主模式中引用它来添加新定义。在我看来,它有点清洁。不过,有些人更喜欢将所有内容放在一个文件中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-04
      • 1970-01-01
      • 2019-09-24
      • 2015-07-18
      • 2016-11-21
      • 2019-12-14
      • 2022-01-02
      • 1970-01-01
      相关资源
      最近更新 更多