关于JSON Schema属性合并扩展、数组项属性复用及$switch内部使用的咨询
我来逐个解答你的问题,都是JSON Schema里很常见的场景:
问题1:如何在JSON Schema中合并或扩展属性?
JSON Schema里最常用的方式是使用allOf关键字来合并多个schema的属性,实现扩展效果。你可以先定义一个基础schema,然后通过allOf引入它,再添加或覆盖额外的属性。
举个例子,先定义基础的用户schema:
{ "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "baseUser": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "name"] } } }
如果要扩展出管理员用户,只需要用allOf包含基础schema,再添加专属属性:
{ "$schema": "http://json-schema.org/draft-07/schema#", "allOf": [ { "$ref": "#/definitions/baseUser" }, { "type": "object", "properties": { "role": { "type": "string", "enum": ["admin", "superadmin"] } }, "required": ["role"] } ] }
如果需要覆盖基础schema里的属性(比如修改name的规则),直接在allOf的后续schema里重新定义该属性即可,后续的规则会覆盖前面的(具体优先级遵循JSON Schema的合并规则)。
问题2:数组项既有共同属性也有差异化属性,能否通过差异化属性扩展/修补共同属性?
完全可以!你可以先定义所有数组项的共同属性schema,然后针对不同类型的项,用allOf结合oneOf/anyOf来实现“基础属性+差异化属性”的结构。
举个例子,数组里的项都有id和type属性,但type为user时需要email,为admin时需要role:
{ "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "commonItem": { "type": "object", "properties": { "id": { "type": "string" }, "type": { "type": "string", "enum": ["user", "admin"] } }, "required": ["id", "type"] }, "userItem": { "allOf": [ { "$ref": "#/definitions/commonItem" }, { "type": "object", "properties": { "email": { "type": "string", "format": "email" } }, "required": ["email"] } ] }, "adminItem": { "allOf": [ { "$ref": "#/definitions/commonItem" }, { "type": "object", "properties": { "role": { "type": "string", "enum": ["admin", "superadmin"] } }, "required": ["role"] } ] } }, "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/userItem" }, { "$ref": "#/definitions/adminItem" } ] } }
这样数组里的每个项都会先满足共同属性的规则,再根据type匹配对应的差异化属性要求。
问题3:能否在对象属性内部使用$switch?
首先要明确:标准JSON Schema规范中并没有$switch这个关键字——你提到的根层级可用的$switch大概率是某个JSON Schema验证库的扩展(比如Ajv的自定义关键字)。
不过,标准JSON Schema提供了if/then/else关键字,可以在对象属性内部实现条件判断的逻辑,完全能替代$switch的大部分场景。
比如,我们可以在对象的属性定义里,根据其他属性的值来动态调整当前属性的规则:
{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "accountType": { "type": "string", "enum": ["personal", "business"] }, "taxId": { "type": "string", "if": { "properties": { "accountType": { "const": "business" } } }, "then": { "minLength": 9 }, "else": { "not": {} } // 个人账号不需要taxId,这里用not{}表示不允许出现 } } }
如果你的验证库支持$switch扩展(比如Ajv),那通常也可以在属性内部使用它,写法类似根层级,但要注意不同库的具体语法要求。不过更推荐使用标准的if/then/else,因为它的兼容性更好,所有支持Draft 7及以上版本的验证库都能识别。
内容的提问来源于stack exchange,提问作者omarsafwany

