如何在JSON Schema中表示多态对象,实现按type字段动态校验
问题原因分析
你的Schema核心问题出在additionalProperties: false的作用域规则:
JSON Schema draft-07 中,
additionalProperties只能识别同层级properties、patternProperties中声明的属性,无法识别父层级或者其他分支中定义的属性。
你把additionalProperties: false写在了每个then分支内,而分支内的properties只声明了value/min/max,没有声明顶层定义的type属性,所以分支校验生效时,type会被判定为非法额外属性,这就是第一个测试用例提示type未定义的原因。
而第二个测试用例没按预期报错,本质是部分校验器对if/then+additionalProperties组合的处理优先级存在偏差,加上你的规则结构本身有逻辑缺陷,导致分支规则没有完全生效。
修复方案
方案1:兼容draft-07版本的调整
在每个then分支的properties中补充type的约束,让additionalProperties可以识别到type是合法属性:
{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "type": { "type": "string", "enum": ["INT", "PERCENT"] } }, "required": ["type"], "allOf": [ { "if": { "properties": {"type": {"const": "INT"}} }, "then": { "properties": { "type": {"const": "INT"}, "value": {"type": "number", "multipleOf": 1} }, "required": ["value"], "additionalProperties": false } }, { "if": { "properties": {"type": {"const": "PERCENT"}} }, "then": { "properties": { "type": {"const": "PERCENT"}, "value": {"type": "number"}, "min": {"type": "number"}, "max": {"type": "number"} }, "required": ["value", "min", "max"], "additionalProperties": false } } ] }
调整后两个测试用例都会触发预期的校验错误:
- PERCENT类型用例不再报错
type未定义 - INT类型用例会同时触发三个错误:
value不满足multipleOf: 1、min和max是额外不允许的属性。
方案2:升级到更高版本Schema(推荐)
如果可以使用draft 2019-09及以上版本,用unevaluatedProperties替代additionalProperties,可以简化写法,不需要重复声明type:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "type": { "type": "string", "enum": ["INT", "PERCENT"] } }, "required": ["type"], "unevaluatedProperties": false, "allOf": [ { "if": { "properties": {"type": {"const": "INT"}} }, "then": { "properties": { "value": {"type": "number", "multipleOf": 1} }, "required": ["value"] } }, { "if": { "properties": {"type": {"const": "PERCENT"}} }, "then": { "properties": { "value": {"type": "number"}, "min": {"type": "number"}, "max": {"type": "number"} }, "required": ["value", "min", "max"] } } ] }
unevaluatedProperties会识别所有分支中声明的属性,以及顶层声明的type属性,逻辑更简洁,兼容性也在主流校验器中得到了支持。
内容的提问来源于stack exchange,提问作者VorpalSword
相关产品推荐
相关产品推荐

