使用不同的JSON模式验证每个JSON节点

时间:2018-11-25 13:02:55

标签: json jsonschema rapidjson

我正在尝试制作一个系统监视器,该监视器可以由用户高度自定义。通过使用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?

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

编辑:更正的架构:(

2 个答案:

答案 0 :(得分:1)

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

注意:

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

答案 1 :(得分:1)

除了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. 在单独的文件中声明每个定义。这将允许您通过简单地添加一个新文件并在主模式中引用它来添加新定义。在我看来,它有点干净。不过,有些人更喜欢将它们全部保存在一个文件中。