Javascript 如何基于枚举数组的组合验证JSON模式? 鉴于:
假设我正在为联系人定义一个模式。但是,我可以有“主要联系人”、“学生”或两者兼而有之的人;三种选择都有不同的属性。联系人类型在Javascript 如何基于枚举数组的组合验证JSON模式? 鉴于:,javascript,node.js,jsonschema,json-schema-validator,ajv,Javascript,Node.js,Jsonschema,Json Schema Validator,Ajv,假设我正在为联系人定义一个模式。但是,我可以有“主要联系人”、“学生”或两者兼而有之的人;三种选择都有不同的属性。联系人类型在联系人类型:[“主要联系人”、“学生”]的数组中定义,可以是一个,也可以是两个 假设每个联系人类型的字段如下所示: 如果是主要联系人,那么我要电话号码 如果是学生,那么我要名字 如果是学生和主要联系人,那么我想要电话号码和名字 用法 我使用库在Node.js中使用如下代码进行验证: function validator(json_schema){ const
联系人类型:[“主要联系人”、“学生”]
的数组中定义,可以是一个,也可以是两个
假设每个联系人类型的字段如下所示:
- 如果是主要联系人,那么我要电话号码
- 如果是学生,那么我要名字
- 如果是学生和主要联系人,那么我想要电话号码和名字
function validator(json_schema){
const Ajv = require('ajv');
const ajv = new Ajv({allErrors: true});
return ajv.compile(json_schema)
}
const validate = validator(json_schema);
const valid = validate(input);
console.log(!!valid); //true or false
console.log(validate.errors)// object or null
注意:我在使用anyOf时遇到了allErrors:true
的问题,我使用allErrors
的输出将所有缺失/无效字段返回给用户,而不是一次返回一个问题。参考:
模式
我已经编写了下面的模式,如果我选择“学生”或“主要联系人”,它会起作用,但当我通过这两个选项时,它仍然希望针对[“学生”]或[“主要联系人”]进行验证,而不是同时对这两个选项进行验证
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"required": [],
"properties": {},
"allOf": [
{
"if": {
"properties": {
"contact_type": {
"contains": {
"allOf": [
{
"type": "string",
"const": "Primary Contact"
},
{
"type": "string",
"const": "Student"
}
]
}
}
}
},
"then": {
"additionalProperties": false,
"properties": {
"contact_type": {
"type": "array",
"items": [
{
"type": "string",
"enum": [
"Student",
"Primary Contact"
]
}
]
},
"phone": {
"type": "string"
},
"first_name": {
"type": "string"
}
},
"required": [
"phone",
"first_name"
]
}
},
{
"if": {
"properties": {
"contact_type": {
"contains": {
"type": "string",
"const": "Student"
}
}
}
},
"then": {
"additionalProperties": false,
"properties": {
"contact_type": {
"type": "array",
"items": [
{
"type": "string",
"enum": [
"Student",
"Primary Contact"
]
}
]
},
"first_name": {
"type": "string"
}
},
"required": [
"first_name"
]
}
},
{
"if": {
"properties": {
"contact_type": {
"contains": {
"type": "string",
"const": "Primary Contact"
}
}
}
},
"then": {
"additionalProperties": false,
"properties": {
"contact_type": {
"type": "array",
"items": [
{
"type": "string",
"enum": [
"Student",
"Primary Contact"
]
}
]
},
"phone": {
"type": "string"
}
},
"required": [
"phone"
]
}
}
]
}
有效输入示例:
- 仅针对[“主要联系人”]:
- 仅针对[“学生”]:
- 对于[“主要联系人”、“学生”]
allErrors:true
,我也希望验证这一点,这可能吗?如果没有,我应该如何更改模式
脚注
除非万不得已,否则我不想将“联系人类型”从数组更改为数组。(这是一项要求,但只有在没有其他办法的情况下才能打破)
我不能允许任何附加项,因此我在if语句中完全定义了每个对象,尽管
contact\u type
很常见。如果我将contact\u type
移出,则会收到关于将contact\u type
作为附加项传递的错误消息(它查看If语句的属性,在将其移出公共位置时看不到contact\u type)。这就是我的初始属性对象为空的原因。以下是我解决验证问题的方法:
这是模式。。。
(如果你试图消除顾虑,会更容易)
首先,我们想定义我们的条件检查子模式
"definitions": {
"is_student": {
"properties": {
"contact_type": {
"contains": {
"const": "Student"
}
}
}
},
"is_primay_contact": {
"properties": {
"contact_type": {
"contains": {
"const": "Primary Contact"
}
}
}
}
},
接下来,我假设您总是希望联系方式
"required": ["contact_type"],
"properties": {
"contact_type": {
"type": "array",
"items": {
"enum": ["Primary Contact", "Student"]
}
},
我们需要定义所有允许的属性,以防止附加属性。(draft-07
无法“看穿”涂抹器关键字,如allOf
。您可以使用draft2019-09
及更高版本,但这是另一个故事)
现在,我们需要定义我们的结构约束
"allOf": [
{
如果联系人是学生,则需要姓名
"if": { "$ref": "#/definitions/is_student" },
"then": { "required": ["first_name"] }
},
{
"if": { "$ref": "#/definitions/is_primay_contact" },
"then": { "required": ["phone"] }
},
{
如果联系人是主要联系人,则需要电话
"if": { "$ref": "#/definitions/is_student" },
"then": { "required": ["first_name"] }
},
{
"if": { "$ref": "#/definitions/is_primay_contact" },
"then": { "required": ["phone"] }
},
{
但是,另外,如果联系人既是学生又是主要联系人
"if": {
"allOf": [
{ "$ref": "#/definitions/is_student" },
{ "$ref": "#/definitions/is_primay_contact" }
]
},
然后我们需要电话和名字
"then": {
"required": ["phone", "first_name"]
},
否则,电话或名字中的任何一个都可以(上一节介绍了哪一个)
我不相信这是最干净的方法,但它确实适用于您提供的需求
至于获取验证错误,您可以传回给最终用户。。。考虑到您列出的条件需求,这不是纯JSON模式所能期望的
话虽如此,ajv确实提供了一个扩展来添加自定义错误消息,鉴于我将验证分解为关注点的方式,它可能可以用于添加自定义错误,正如您希望做的那样()。这完全是可能的,我非常乐意提供帮助。乍一看,您至少犯了一个简单的错误,contains
不接受模式对象。幸运的是,你想要的东西可以通过稍微不同的方式实现。我正在为你制定一个解决方案编辑:我对包含的内容错了。。。我需要你的回答。关于“我不相信这是最干净的方法”,当使用“additionalProperties”:false
时,没有干净的方法。有几种选择,但它们都一样糟糕。小nit:最后一个然后是多余的。它可以被移除。此外,在某些情况下,如果向每个定义中添加“required”:[“contact_type”]
,您将获得更好的错误消息。
"if": { "$ref": "#/definitions/is_primay_contact" },
"then": { "required": ["phone"] }
},
{
"if": {
"allOf": [
{ "$ref": "#/definitions/is_student" },
{ "$ref": "#/definitions/is_primay_contact" }
]
},
"then": {
"required": ["phone", "first_name"]
},
"else": {
"oneOf": [
{
"required": ["phone"]
},
{
"required": ["first_name"]
}
]
}
}
]
}