JSON 架构 - 条件验证
JSON Schema - conditional validation
我有以下架构。我已经尽我所能地实现了它,但它仍然没有像我想要的那样工作。
{
"$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-did-they-accept-your-order 的回答。
因此以下输入应该有效:
{
"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"
}
同样,我希望以下输入无法通过验证并针对包含空白字符串的字段抛出 'required' 错误。第一个应该抛出错误,因为 q-why-didn't-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 的答案而引起的。这些问题的答案在表单提交时通过浏览器发布。在浏览器中,布尔问题显示为 yes/no 单选按钮。因此,当用户不检查任何一个无线电,但确实尝试提交表单时,无线电的答案将被完全省略。发送的数据对象如下所示:
{
"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 抛出一个 'required' 错误。它不应该为其他任何事情抛出 'required' 错误,因为 q-how-much-was-the-bill 和 q-why-didn't-they-accept 都不是必需的,除非 q 的相关值-他们是否接受您的订单已被选中。
我的实际结果:
AJV 为所有三个空输入抛出错误。
所以我的问题是,如何让 AJV 验证此模式,以及如何让 AJV 仅在问题未得到回答时抛出必要的错误。
编辑:
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"
}
]
您对 if
/then
模式的应用中缺少一部分。让我们以这个简单的模式为例。
{
"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
.
时适用
我有以下架构。我已经尽我所能地实现了它,但它仍然没有像我想要的那样工作。
{
"$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-did-they-accept-your-order 的回答。
因此以下输入应该有效:
{
"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"
}
同样,我希望以下输入无法通过验证并针对包含空白字符串的字段抛出 'required' 错误。第一个应该抛出错误,因为 q-why-didn't-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 的答案而引起的。这些问题的答案在表单提交时通过浏览器发布。在浏览器中,布尔问题显示为 yes/no 单选按钮。因此,当用户不检查任何一个无线电,但确实尝试提交表单时,无线电的答案将被完全省略。发送的数据对象如下所示:
{
"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 抛出一个 'required' 错误。它不应该为其他任何事情抛出 'required' 错误,因为 q-how-much-was-the-bill 和 q-why-didn't-they-accept 都不是必需的,除非 q 的相关值-他们是否接受您的订单已被选中。
我的实际结果:
AJV 为所有三个空输入抛出错误。
所以我的问题是,如何让 AJV 验证此模式,以及如何让 AJV 仅在问题未得到回答时抛出必要的错误。
编辑:
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"
}
]
您对 if
/then
模式的应用中缺少一部分。让我们以这个简单的模式为例。
{
"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
.