Схема JSON - условная проверка

У меня есть следующая схема. Я реализовал его как можно лучше, но он все еще не работает так, как мне хотелось бы.

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "title": "Ordering pizza",
    "propertyNames": {
        "enum": [
            "q-who-did-you-order-from",
            "q-did-they-accept-your-order",
            "q-how-much-was-the-bill",
            "q-why-didnt-they-accept"
        ]
    },
    "properties": {
        "q-who-did-you-order-from": {
            "type": "string",
            "title": "Who have you ordered pizza from?",
            "maxLength": 50,
            "errorMessages": {
                "required": "Enter who you ordered from",
                "maxLength":
                    "Who you ordered from must be 50 characters or less"
            }
        },
        "q-did-they-accept-your-order": {
            "title": "Have they accepted your order?",
            "type": "boolean",
            "errorMessages": {
                "required":
                    "Select yes if they have accepted your order"
            }
        },
        "q-how-much-was-the-bill": {
            "type": "string",
            "title": "How much was the bill?",
            "maxLength": 50,
            "errorMessages": {
                "required": "Enter an amount",
                "maxLength": "Amount must be 50 characters or less"
            }
        },
        "q-why-didnt-they-accept": {
            "type": "string",
            "title": "Why wasnt your order accepted?",
            "description":
                "If you do not know you can say so.",
            "maxLength": 50,
            "errorMessages": {
                "required": "Enter a reason",
                "maxLength": "Reason must be 50 characters or less"
            }
        }
    },
    "required": ["q-who-did-you-order-from", "q-did-they-accept-your-order"],
    "allOf": [
        {
            "$ref": "#/definitions/if-false-then-q-why-didnt-they-accept-is-required"
        },
        {
            "$ref": "#/definitions/if-true-then-q-how-much-was-the-bill-is-required"
        }
    ],
    "definitions": {
        "if-false-then-q-why-didnt-they-accept-is-required": {
            "if": {
                "properties": {
                    "q-did-they-accept-your-order": {
                        "const": false
                    }
                }
            },
            "then": {
                "required": ["q-why-didnt-they-accept"],
                "propertyNames": {
                    "enum": [
                        "q-who-did-you-order-from",
                        "q-did-they-accept-your-order",
                        "q-why-didnt-they-accept"
                    ]
                }
            }
        },
        "if-true-then-q-how-much-was-the-bill-is-required": {
            "if": {
                "properties": {
                    "q-did-they-accept-your-order": {
                        "const": true
                    }
                }
            },
            "then": {
                "required": ["q-how-much-was-the-bill"],
                "propertyNames": {
                    "enum": [
                        "q-who-did-you-order-from",
                        "q-did-they-accept-your-order",
                        "q-how-much-was-the-bill"
                    ]
                }
            }
        }
    }
}

Ожидается, что пользователь введет значение для q-who-did-you-order-from и q-did-they-accept-your-order, а затем только один из двух оставшихся вопросов, основанный на их ответе на q- они приняли ваш заказ.

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

{
    "q-did-you-order-from": "Pizza hut",
    "q-did-they-accept-your-order": "true",
    "q-how-much-was-the-bill": "20"
}



{
    "q-did-you-order-from": "Pizza hut",
    "q-did-they-accept-your-order": "false",
    "q-why-didn't-they-accept": "Incorrect card details"
}

Точно так же я ожидал, что следующие входные данные не пройдут проверку и выдадут «обязательную» ошибку для поля, содержащего пустую строку. Первый должен выдать ошибку, потому что q-why-not-they-accept пусто:

{
    "q-did-you-order-from": "Pizza hut",
    "q-did-they-accept-your-order": "false",
    "q-why-didn't-they-accept": ""
}

И это должно выдать ошибку, потому что q-how-much-was-the-bill пусто.

{
    "q-did-you-order-from": "Pizza hut",
    "q-did-they-accept-your-order": "true",
    "q-how-much-was-the-bill": ""
}

И это так! Это работает, как ожидалось. Однако мы обнаружили ошибку, которая возникает из-за того, что пользователь не вводит ответ на вопрос q-did-they-accept-your-order. Ответы на эти вопросы отправляются через браузер при отправке формы. В браузере логический вопрос представлен как переключатели "да / нет". В результате, когда пользователь не проверяет ни одно радио, но пытается отправить форму, ответ для радио полностью опускается. Отправленный объект данных выглядит так:

{
    "q-did-you-order-from": "Pizza hut",
    "q-how-much-was-the-bill": "",
    "q-why-didn't-they-accept": "",
}

Мой ОЖИДАЕМЫЙ результат здесь:

AJV выдает только одну «обязательную» ошибку для q-did-they-accept-your-order. Он не должен выдавать «обязательную» ошибку ни для чего другого, поскольку и q-how-much-was-the-bill, и q-why-not-they-accept не требуются, если только соответствующее значение для q-did -они-принимают-ваш-заказ выбраны.

Мой ФАКТИЧЕСКИЙ результат:

AJV выдает ошибку для всех трех пустых входов.

Итак, мой вопрос: как мне заставить AJV проверять эту схему и ТОЛЬКО для q-did-they-accept-your-order выдавать требуемую ошибку, если на вопрос нет ответа.

РЕДАКТИРОВАТЬ:

Вывод AJV выглядит следующим образом:

[
    {
        "keyword": "required",
        "dataPath": "",
        "schemaPath": "#/required",
        "params": {
            "missingProperty": "q-did-they-accept-your-order"
        },
        "message": "should have required property 'q-did-they-accept-your-order'"
    },
    {
        "keyword": "required",
        "dataPath": "",
        "schemaPath": "#/definitions/if-false-then-q-why-didnt-they-accept-is-required",
        "params": {
            "missingProperty": "q-why-didnt-they-accept"
        },
        "message": "should have required property 'q-why-didnt-they-accept'"
    },
    {
        "keyword": "if",
        "dataPath": "",
        "schemaPath": "#/definitions/if-false-then-q-why-didnt-they-accept-is-required/if",
        "params": {
            "failingKeyword": "then"
        },
        "message": "should match \"then\" schema"
    },
    {
        "keyword": "required",
        "dataPath": "",
        "schemaPath": "#/definitions/if-true-then-q-how-much-was-the-bill-is-required/then/required",
        "params": {
            "missingProperty": "q-how-much-was-the-bill"
        },
        "message": "should have required property 'q-how-much-was-the-bill'"
    },
    {
        "keyword": "if",
        "dataPath": "",
        "schemaPath": "#/definitions/if-true-then-q-how-much-was-the-bill-is-required/if",
        "params": {
            "failingKeyword": "then"
        },
        "message": "should match \"then\" schema"
    }
]

person Barry Piccinni    schedule 03.06.2019    source источник
comment
Какие ошибки он выдает? Не могли бы вы отредактировать свою схему, чтобы она была действительной JSON, а не javascript, пожалуйста? (требует двойных кавычек вокруг клавиш). Это упростит отладку и тестирование с помощью jsonschema.dev, который использует ajv.   -  person Relequestual    schedule 03.06.2019
comment
@Relequestual Спасибо за ответ, я отредактировал свой вопрос в соответствии с вашим запросом.   -  person Barry Piccinni    schedule 03.06.2019


Ответы (1)


В вашем приложении шаблона _1 _ / _ 2_ отсутствует часть. Давайте использовать эту простую схему в качестве примера.

{
  "if": {
    "properties": {
      "foo": { "const": true }
    }
  },
  "then": {
    "required": ["bar"]
  }
}

Если я проверю {} на соответствие этой схеме, он не сможет сказать, что свойство «bar» является обязательным. Поскольку схема /if не требует свойства "foo", {} является допустимым, и поэтому применяется схема /then. Чтобы решить эту проблему, вам просто нужно сделать свойство "foo" обязательным.

{
  "if": {
    "properties": {
      "foo": { "const": true }
    },
    "required": ["foo"]
  },
  "then": {
    "required": ["bar"]
  }
}

Теперь {} действителен для схемы. схема /then будет применяться только в том случае, если есть свойство "foo" и его значение равно true.

person Jason Desrosiers    schedule 05.06.2019