Как указать, если свойство не должно существовать или содержать ноль? - PullRequest
2 голосов
/ 11 марта 2020

Год go Я спросил , как установить тип объекта схемы на основе значения другого свойства? , на который у меня есть отличный ответ, и я с тех пор использовал эту схему.

Теперь исходные данные изменились - и схема терпит неудачу при следующих обстоятельствах:

Исходные данные содержат много свойств, однако только два из них актуальны для этого вопроса: «ключ» и «значение» - тип «значения» зависит от значения «ключ» -

Например:
Если ключ «комментарий», тип значение {"Text":"commentValue"}.
Если ключ «смещен», тип значения равен {"seconds":int}.
Если ключ «погодный», тип значения равен {"value": Enum["sun", "clouds", "rain"...]}

Некоторые ключи не имеют свойства value, поэтому схема должна запрещать его появление с этими ключами - например, если ключ находится в режиме ожидания, свойство value вообще не должно появляться - что и делает моя текущая схема хорошо.

Однако теперь источник данных изменился, и я получаю "value" :{} как часть моего Json, где однажды он был опущен - и текущая схема не позволяет этого.

Так мой вопрос - как мне разрешить один из этих двух вариантов? Я пробовал любую комбинацию anyOf, которую мог придумать, но с треском провалился - Newtonsoft.Json.Schema.JSchema не удалось разобрать текст.

Вот упрощенная версия схемы, которую я сейчас использую:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "TestOptionalObject",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "test"
  ],
  "properties": {
    "test": {
      "$ref": "#/definitions/test"
    }
  },
  "definitions": {
    "test": {
      "type": "object",
      "required": [
        "key"
      ],
      "properties": {
        "key": {
          "type": "string",
          "enum": [
            "comment",
            "offset",
            "standby",
            "status_unsure",
            "status_ok"
          ]
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "event": {
                "enum": [
                  "standby",
                  "status_unsure",
                  "status_ok"
                ]
              }
            }
          },
          "then": {
            "properties": {
              "value": false
            }
          }
        },
        {
          "if": {
            "properties": {
              "key": {
                "const": "comment"
              }
            }
          },
          "then": {
            "properties": {
              "value": {
                "$ref": "#/definitions/commentValue"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "key": {
                "const": "offset"
              }
            }
          },
          "then": {
            "properties": {
              "value": {
                "$ref": "#/definitions/offsetValue"
              }
            }
          }
        }
      ]
    },
    "commentValue": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "text"
      ],
      "properties": {
        "text": {
          "type": "string"
        }
      }
    },
    "offsetValue": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "seconds"
      ],
      "properties": {
        "seconds": {
          "type": "integer",
          "format": "int32"
        }
      }
    }
  }
}

Вот некоторые из вещей, которые я пробовал:

"then": {
  "properties": {
    "anyOf": [
      { "value": false },
      { "value": null }
    ]
  }
}
"then": {
  "anyOf": [
  {
    "properties": {
      { "value": false }
    },
    "properties": {
      { "value": null }
    }
  ]  
}
"then": {
  "properties": {
    "value": 
      "anyOf": [false, null ]
  }
}

Json примеров для проверки:

В случае ошибки:

{
  "test": {
    "key": "comment",
      "value": {"seconds":12}
  }
}

{
  "test": {
    "key": "standby",
     "value": {"asdf":12}
  }
}

Должно пройти:

{
  "test": {
    "key": "comment",
     "value": {"text":"comment text"}
  }
}


{
  "test": {
    "key": "offset",
     "value": {"seconds":12}
  }
}

{
  "test": {
    "key": "standby"
  }
}

{
  "test": {
    "key": "standby",
     "value": {}
  }
}

Обратите внимание на последний пример - свойство value является пустым объектом - оно также должно пройти, но не с текущей схемой, поскольку value для этого ключа свойство вообще не должно существовать.

1 Ответ

1 голос
/ 12 марта 2020

Ты близко, но не совсем. Вы должны помнить, что allOf - это массив подсхем (JSON Схемы). (Нулевая недопустимая схема, поэтому у вас могут быть ошибки «недопустимая схема».)

Таким образом, рассмотрите эту модифицированную подсхему из allOf[0] ...

{
  "if": {
    "properties": {
      "key": {
        "enum": [
          "standby",
          "status_unsure",
          "status_ok"
        ]
      }
    }
  },
  "then": {
    "properties": {
      "value": {
        "type": [
          "object"
        ],
        "additionalProperties": false
      }
    }
  }
}

Вы можете проверить это здесь: https://jsonschema.dev/s/EfNI1

Блок if остается прежним. (Хотя я исправил то, что, как я полагаю, было ошибкой при использовании event вместо key в вашем упрощении от реальной схемы.)

Блок then должен определить, что объект (уже проверенный definitions.test) имеет ключ value, где значение value является объектом и не имеет свойств (он же пустой объект).

Для достижения «пустой объект», вам нужно использовать additionalProperties.

  • additionalProperties применяет его подсхему значения ко всем существующим свойствам, которые не были определены в properties или которые соответствуют ключам (регулярное выражение) из patternProperties.
  • false, поскольку схема всегда не проходит проверку.

additionalProperties без properites применяется ко ВСЕМ свойствам и, следовательно, со значением ложь, проверяет "является пустым объектом".

...