面向切面结构的JSON如何编写对应JSON Schema完成校验
你之前写的Schema用了元组形式的items配置,这类配置只会校验数组对应位置的元素,不会强制要求数组包含所有必填项,也不会限制数组最小长度,所以才会出现空数组、缺必填项也能通过校验的问题,另外你还把metadata键名误写为驼峰的metaData,也会导致匹配失效。
以下是两种可行的校验方案:
方案一:用Draft 2019-09的contains关键字实现(无需大量if/then)
这个方案支持元素任意顺序,不需要写复杂的条件判断,只要你的校验器支持JSON Schema Draft 2019-09及以上版本(目前主流校验器如Ajv、Hyperjump都支持)就可以用。
核心逻辑是:
- 强制数组必须恰好包含1次
nv、metadata、status三类对象 - 所有额外的元素必须是你预定义的动态键对象(nodes/edges等)
- 如果需要严格匹配
metadata里声明的name,可结合$data扩展(Ajv支持)实现完全动态校验,不需要提前写死条件
示例Schema如下:
{ "$schema": "https://json-schema.org/draft/2019-09/schema", "type": "array", "minItems": 3, "allOf": [ // 强制必须存在1个nv对象 { "contains": { "type": "object", "properties": { "nv": { "type": "array", "items": { "$ref": "#/$defs/nv" } } }, "required": ["nv"], "additionalProperties": false }, "minContains": 1, "maxContains": 1 }, // 强制必须存在1个metadata对象 { "contains": { "type": "object", "properties": { "metadata": { "type": "array", "items": { "$ref": "#/$defs/metadataItem" } } }, "required": ["metadata"], "additionalProperties": false }, "minContains": 1, "maxContains": 1 }, // 强制必须存在1个status对象 { "contains": { "type": "object", "properties": { "status": { "type": "array", "items": { "$ref": "#/$defs/status" } } }, "required": ["status"], "additionalProperties": false }, "minContains": 1, "maxContains": 1 } ], // 所有元素都必须属于以下允许的类型 "items": { "type": "object", "oneOf": [ { "properties": {"nv": {"type": "array", "items": { "$ref": "#/$defs/nv" }}}, "required": ["nv"] }, { "properties": {"metadata": {"type": "array", "items": { "$ref": "#/$defs/metadataItem" }}}, "required": ["metadata"] }, { "properties": {"status": {"type": "array", "items": { "$ref": "#/$defs/status" }}}, "required": ["status"] }, // 下面是所有允许的动态键 { "properties": {"nodes": {"type": "array", "items": { "$ref": "#/$defs/nodes" }}}, "required": ["nodes"] }, { "properties": {"edges": {"type": "array", "items": { "$ref": "#/$defs/edges" }}}, "required": ["edges"] }, { "properties": {"nodeAttribute": {"type": "array", "items": { "$ref": "#/$defs/nodeAttribute" }}}, "required": ["nodeAttribute"] }, { "properties": {"edgeAttribute": {"type": "array", "items": { "$ref": "#/$defs/edgeAttribute" }}}, "required": ["edgeAttribute"] } ], "additionalProperties": false }, "$defs": { "nv": { "type": "object", "properties": {"ln": {"type": "number"}}, "required": ["ln"] }, "metadataItem": { "type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"] }, "status": { "type": "object", "properties": {"success": {"type": "boolean"}}, "required": ["success"] }, "nodes": { /* 你的nodes结构定义 */ }, "edges": { /* 你的edges结构定义 */ }, "nodeAttribute": { /* 你的nodeAttribute结构定义 */ }, "edgeAttribute": { /* 你的edgeAttribute结构定义 */ } } }
如果要实现和metadata声明的name完全匹配(比如metadata里写了3个name,就必须有3个对应键的对象),只要你用的校验器支持$data扩展,在上面的Schema里加几个规则就可以,不需要修改其他逻辑。
方案二:用if/then/else实现标准JSON Schema兼容方案
如果你需要兼容更低版本的JSON Schema,或者不能用$data扩展,也可以用if/then/else实现:
每个可能的name字段值对应一条规则,示例如下:
{ // 上面的基础规则不变,新增规则加在allOf数组里 "allOf": [ // 原来的三个contains规则保留,加下面的条件 { "if": { "items": { "properties": { "metadata": { "contains": {"properties": {"name": {"const": "nodes"}}}, "minContains": 1 } } } }, "then": { "contains": { "properties": {"nodes": {}}, "required": ["nodes"] }, "minContains":1, "maxContains":1 } }, // 每个可能的name都按上面的格式加一条规则即可 ] }
即使metadata的条目多,每条规则的结构都是固定的,批量生成也很方便。
内容的提问来源于stack exchange,提问作者Hassan Assaad
相关产品推荐
相关产品推荐

