如何用JSON Schema验证数组对象中指定键值的唯一性?
这个问题确实戳中了uniqueItems的痛点——它只校验整个数组元素的完全重复,要是只是某个字段撞了、其他字段不一样,它就完全不管用了。我给你两种靠谱的解决方案,适配不同的JSON Schema版本:
方案1:用JSON Schema 2020-12的$data关键字(推荐)
JSON Schema 2020-12新增的$data关键字可以直接引用实例里的数据,结合uniqueItems就能轻松实现特定字段的唯一性校验。思路是先把数组里所有Name字段的值提取成一个新数组,再验证这个数组没有重复值。
示例Schema:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "test_array": { "type": "array", "items": { "type": "object", "properties": { "Name": { "type": "string" }, "Description": { "type": "string" } }, "required": ["Name", "Description"] }, "allOf": [ { "$dynamicRef": "#/$defs/unique-names" } ] } }, "$defs": { "unique-names": { "type": "array", "uniqueItems": true, "items": { "$data": "0/Name" } } } }
这里的"$data": "0/Name"是核心:它会遍历数组里的每个元素,取出每个元素的Name值,生成一个纯Name的数组,然后uniqueItems: true就会确保这个数组里没有重复项。
方案2:兼容旧版本(比如Draft-07)的写法
如果你的验证工具还不支持2020-12版本,也可以用allOf+not+contains的组合来实现,核心逻辑是:不允许数组里存在两个不同的元素,它们的Name值相同。
示例Schema:
{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "test_array": { "type": "array", "items": { "type": "object", "properties": { "Name": { "type": "string" }, "Description": { "type": "string" } }, "required": ["Name", "Description"], "not": { "contains": { "$ref": "#", "properties": { "Name": { "const": { "$data": "1/Name" } } }, "required": ["Name"] } } } } } }
简单说就是:对数组里的每一项,检查数组中没有其他项的Name和它一样。"$data": "1/Name"用来引用当前项的Name值,contains负责查找是否有匹配的其他项,最后用not把这种重复情况排除掉。
验证效果
针对你给出的测试用例:
{ "test_array": [ { "Name": "name1", "Description": "unique_desc_1" }, { "Name": "name1", "Description": "unique_desc_2" } ] }
上面两种Schema都会校验失败,因为Name字段重复了;而如果Name都是唯一的,哪怕Description重复,校验也会顺利通过。
需要注意的是,$data关键字的支持要看你用的验证工具,像Ajv、JSON Schema Validator这些主流工具都已经支持2020-12的特性,优先选方案1会更简洁直观。
内容的提问来源于stack exchange,提问作者TakingItCasual

