JSON проверка схемы объектов условного массива - PullRequest
1 голос
/ 15 апреля 2020

У меня есть два типа наборов данных, с данными CSV или фиксированной длины. В данных csv список полей - это просто список имен, в то время как в данных фиксированной длины каждое поле определяется fieldName и fieldLength. Мне нужна схема json для проверки обоих случаев, но после попытки нескольких решений, включая эти , я не уверен, что это можно сделать. Или, может быть, мое понимание схемы JSON все еще далеко от совершенства.

json:

{
      "dataset": "csv data",
      "dataFormat": "csv",
      "fieldList": [{
                  "fieldName": "id"
            },
            {
                  "fieldName": "num"
            },
            {
                  "fieldName": "struct"
            }
      ]
}

{
    "dataset": "fixed length",
    "dataFormat": "fixed",
    "fieldList": [{
            "fieldName": "id",
            "fieldLength": 13
        },
        {
            "fieldName": "num"
        },
        {
            "fieldName": "struct"
        }
    ]
}

JSON схема:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": [
    "dataset",
    "dataFormat",
    "fieldList"
  ],
  "properties": {
    "dataset": {
      "$id": "#/properties/dataset",
      "type": "string"
    },
    "dataFormat": {
      "$id": "#/properties/dataFormat",
      "type": "string",
      "enum": [
        "csv",
        "fixed"
      ]
    },
    "fieldList": {
      "$id": "#/properties/fieldList",
      "type": "array",
      "additionalItems": true,
      "items": {
        "$id": "#/properties/fieldList/items",
        "type": "object",
        "additionalProperties": true,
        "required": [
          "fieldName"
        ],
        "if": {
          "properties": {
            "dataFormat": {
              "const": "fixed"
            }
          }
        },
        "then": {"items":{
          "required": [
            "fieldLength"
          ]}
        },
        "properties": {
          "fieldName": {
            "$id": "#/properties/fieldList/items/properties/fieldName",
            "type": "string"
          },
          "fieldLength": {
            "$id": "#/properties/fieldList/items/properties/fieldLength",
            "type": "integer"
          }
        }
      }
    }
  }
}

Оба документа положительно проверены, даже если в «фиксированном» типе только первый элемент включает в себя обязательное полеLength. Любое предложение ?

1 Ответ

2 голосов
/ 16 апреля 2020

В вашей схеме есть несколько вещей, которые можно улучшить:

  1. if / then находится не в том месте. Прямо сейчас, if ищет свойство "dataFormat" внутри "fieldList" items и никогда не находит его. then также пытается навязать существование свойства "fieldLength" в "fieldList".items.items (а поскольку "fieldList".items является object, а не array, это просто будет проигнорировано.
  2. Вы должны удалить атрибут additionalItems. Чтобы процитировать json -schema.org :

    Когда items является одной схемой, ключевое слово additionalItems не имеет смысла, и это не должен использоваться.

  3. Вы можете удалить атрибут additionalProperties, поскольку его значение по умолчанию уже true. Другая цитата из json -schema.org :

    Ключевое слово additionalProperties используется для управления обработкой лишних вещей, то есть properties, имена которых не указаны в ключевом слове свойств. По умолчанию любые дополнительные свойства разрешены.

  4. Атрибуты $id не приносят большой пользы (и не совместимы с более новым проектом 2019-09, где они будут разрешены только в новом ключевом слове $anchor). Вы можете хотите их пропустить.

Ваша главная проблема - точка № 1 здесь. Вы сможете достичь того, чего хотите, добавив что-то вроде этого на верхнем уровне:

"oneOf": [
  {
    "properties": {
      "dataFormat": { "const": "csv" }
    }
  },
  {
    "properties": {
      "dataFormat": { "const": "fixed" },
      "fieldList": {
        "items": {
          "required": ["fieldLength"]
        }
      }
    }
  }
]

Полная схема может выглядеть следующим образом:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": [
    "dataset",
    "dataFormat",
    "fieldList"
  ],
  "properties": {
    "dataset": {
      "$id": "#/properties/dataset",
      "type": "string"
    },
    "dataFormat": {
      "$id": "#/properties/dataFormat",
      "type": "string",
      "enum": [
        "csv",
        "fixed"
      ]
    },
    "fieldList": {
      "$id": "#/properties/fieldList",
      "type": "array",
      "items": {
        "$id": "#/properties/fieldList/items",
        "type": "object",
        "required": [
          "fieldName"
        ],
        "properties": {
          "fieldName": {
            "$id": "#/properties/fieldList/items/properties/fieldName",
            "type": "string"
          },
          "fieldLength": {
            "$id": "#/properties/fieldList/items/properties/fieldLength",
            "type": "integer"
          }
        }
      }
    }
  },
  "oneOf": [
    {
      "properties": {
        "dataFormat": {
          "const": "csv"
        }
      }
    },
    {
      "properties": {
        "dataFormat": {
          "const": "fixed"
        },
        "fieldList": {
          "items": {
            "required": [
              "fieldLength"
            ]
          }
        }
      }
    }
  ]
}

Для полноты картины : вы можете достичь того же с if / then, а также:

"if": {
  "properties": {
    "dataFormat": {
      "const": "fixed"
    }
  }
},
"then": {
  "properties": {
    "fieldList": {
      "items": {
        "required": [
          "fieldLength"
        ]
      }
    }
  }
}
...