JSON Schema / Ajv - 如何验证字符串数组的组合长度?
JSON Schema / Ajv - How to validate the combined lengths of an array of strings?
假设我需要验证订单的送货地址:
{ "id": 1
, "recipient": "John Doe"
, "shipping_address": [ "address line 1"
, "address line 2"
, "address line 3"
]
}
地址规则是:
- 字符串数组
- 至少一个字符串(并且可以有尽可能多的字符串)
- 所有字符串的总长度不得超过 X 个字符(例如 50 个字符)
我可以使用以下 JSON 架构实现 1 和 2:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
}
问题:如何使用 JSON Schema / Ajv 实现条件 3?
我找到了使用 Ajv custom keywords 设计以下架构的解决方案:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}
诀窍是在 Ajv:
中添加对 maxCombinedLength
的支持
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data) => {
return data.reduce((l, s) => l + s.length, 0) <= schema;
}
});
其中:
ajv
是 Ajv 的实例
schema
是 50
data
是 shipping_address
数组
演示版
const validate =
ajv.compile({ type: 'array'
, items: { type: 'string' }
, minItems: 1
, maxCombinedLength: 50
});
console.log(
validate([]));
// false (need at least one item)
console.log(
validate([ "address line 1"
, "address line 2"
, "address line 3"
]));
// true
console.log(
validate([ "address line 1"
, "address line 2"
, "address line 3"
, "address line 4"
, "address line 5"
, "address line 6"
, "address line 7"
, "address line 8"
])
, validate.errors);
// false (maxCombinedLength not satisfied!)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.12.0/ajv.min.js"></script>
<script>
const ajv = new Ajv;
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data, parent_schema) => {
return data.reduce((l, s) => l + s.length, 0) <= schema;
}
});
</script>
附录:如何忽略非字符串数组?
显然我们不能将 maxCombinedLength
与对象数组一起使用。
谢天谢地,Ajv 允许我们访问父模式:
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data, parent_schema) => {
if (parent_schema.items.type === 'string') {
return data.reduce((l, s) => l + s.length, 0) <= schema;
} else {
return true;
}
}
});
因此具有以下架构:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}
自定义关键字函数将接收 50
作为 schema
参数,数组作为 data
参数,完整模式作为 parent_schema
参数。
parent_schema
参数用于查询数组的类型。如果我们不需要字符串,我们可以通过返回 true
.
来使 maxCombinedLength
关键字无效
假设我需要验证订单的送货地址:
{ "id": 1
, "recipient": "John Doe"
, "shipping_address": [ "address line 1"
, "address line 2"
, "address line 3"
]
}
地址规则是:
- 字符串数组
- 至少一个字符串(并且可以有尽可能多的字符串)
- 所有字符串的总长度不得超过 X 个字符(例如 50 个字符)
我可以使用以下 JSON 架构实现 1 和 2:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
}
问题:如何使用 JSON Schema / Ajv 实现条件 3?
我找到了使用 Ajv custom keywords 设计以下架构的解决方案:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}
诀窍是在 Ajv:
中添加对maxCombinedLength
的支持
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data) => {
return data.reduce((l, s) => l + s.length, 0) <= schema;
}
});
其中:
ajv
是 Ajv 的实例
schema
是50
data
是shipping_address
数组
演示版
const validate =
ajv.compile({ type: 'array'
, items: { type: 'string' }
, minItems: 1
, maxCombinedLength: 50
});
console.log(
validate([]));
// false (need at least one item)
console.log(
validate([ "address line 1"
, "address line 2"
, "address line 3"
]));
// true
console.log(
validate([ "address line 1"
, "address line 2"
, "address line 3"
, "address line 4"
, "address line 5"
, "address line 6"
, "address line 7"
, "address line 8"
])
, validate.errors);
// false (maxCombinedLength not satisfied!)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.12.0/ajv.min.js"></script>
<script>
const ajv = new Ajv;
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data, parent_schema) => {
return data.reduce((l, s) => l + s.length, 0) <= schema;
}
});
</script>
附录:如何忽略非字符串数组?
显然我们不能将 maxCombinedLength
与对象数组一起使用。
谢天谢地,Ajv 允许我们访问父模式:
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data, parent_schema) => {
if (parent_schema.items.type === 'string') {
return data.reduce((l, s) => l + s.length, 0) <= schema;
} else {
return true;
}
}
});
因此具有以下架构:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}
自定义关键字函数将接收 50
作为 schema
参数,数组作为 data
参数,完整模式作为 parent_schema
参数。
parent_schema
参数用于查询数组的类型。如果我们不需要字符串,我们可以通过返回 true
.
maxCombinedLength
关键字无效