Проверяйте каждый узел JSON с помощью другой схемы JSON.

Я пытаюсь сделать системный монитор, который настраивается пользователем. Эта настройка достигается за счет использования файла 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 таким образом?

Все отзывы о том, как решить эту проблему, будут оценены.

Изменить: исправлена ​​схема :(


person Kwan    schedule 25.11.2018    source источник
comment
Этот вопрос не имеет ничего общего с C++.   -  person Ulrich Eckhardt    schedule 25.11.2018


Ответы (2)


С этим связан простой подход: используйте объявление шаблона oneOf, чтобы указать расположение элементов массива. Внутри этих вложенных объявлений вы указываете фиксированный идентификатор (возможно, содержимое вашего поля _) как константу, так что существует только одна вложенная схема, соответствующая каждому из ваших типов панелей.

Заметки:

  • Мне пришлось указать идентификатор типа константы с помощью спецификатора enum, потому что обычный спецификатор constant не работал с библиотекой, которую я использовал. Возможно, это также было упущением при пересмотре спецификации, на которой он был основан.
  • Другой подход заключается в разделении этапов проверки. Вы просто проверяете, что элементы массива являются объектами и что они имеют строковое поле _, содержащее один из поддерживаемых типов. При переборе массива вы затем проверяете каждое поле отдельно в соответствии с его полем _.
person Ulrich Eckhardt    schedule 25.11.2018

В дополнение к ответу Ульриха, вот пример того, что я бы сделал:

{
  "$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, которое требует, чтобы все элементы также соответствовали этой схеме (что дает нам рекурсивное поведение). На самом деле это мало что дает, за исключением того, что позволяет нам объявлять эти вещи в одном месте вместо того, чтобы объявлять их в трех других определениях.

(Обратите внимание, что мы не объявляем _ в списке свойств. Это означает, что любое значение будет передаваться для этой части схемы. Мы очистим его в следующей части. Если вы хотите убедиться, что будущие компоненты объявляются строками, тогда вы можете добавить требование "type": "string" к этому свойству, но я не считаю это необходимым, если другие не создают эти компоненты.)

Во-вторых, мы объявляем каждый из наших конкретных типов как отдельные определения, используя ключевое слово const, чтобы изолировать тот, который нам нужен. Эта конструкция аналогична оператору switch (или case). Если экземпляр не соответствует ни одному из этих явных параметров, происходит сбой. Если отсутствует одно из обязательных базовых свойств, происходит сбой.

Это приведет вас туда, где вы хотите быть.

Чтобы продвинуться дальше, вы можете сделать еще две вещи:

  1. Добавьте required к другим определениям, чтобы сказать, что определенные свойства также требуются (например, freq_Unit для определения cpu).
  2. Объявите каждое из определений в отдельных файлах. Это позволит вам добавить новое определение, просто добавив новый файл и сославшись на него в основной схеме. На мой взгляд, стало немного чище. Однако некоторые люди предпочитают иметь все это в одном файле.
person gregsdennis    schedule 26.11.2018