使用oneOf约束产品属性时无法限制非对应属性的问题
解决JSON Schema oneOf约束失效问题,确保产品属性严格匹配
问题背景
定义了3类产品,期望通过JSON Schema的oneOf关键字根据属性匹配对应产品,但当前Schema无法约束非对应产品的属性,导致不符合规则的JSON实例能通过验证。
原Schema尝试
{ "type": "object", "required": [ "type", "productId" ], "additionalProperties": false, "properties": { "productId": { "type": "string", "minLength": 1 }, "type": { "enum": ["product"] }, "productInfo": { "type": "object", "additionalProperties": false, "required": [ "type", "gameMode"], "properties": { "type": { "enum": ["digital"] }, "gameMode": { "type": "object", "oneOf": [ { "$ref": "#/$defs/audioPlayer" }, { "$ref": "#/$defs/multiPlayer" }, { "$ref": "#/$defs/console" }, { "$ref": "#/$defs/controller" } ] } } } }, "$defs": { "audioPlayer": { "type": "object", "gameMode": { "enum": ["mp3", "wav"] } }, "controller": { "type": "object", "properties": { "mode": { "enum": ["Retro","XboxElite" ] }, "batteryLevel": { "type": "string" }, "warranty": { "type": "string" }, "tradeIn": { "type": "boolean" } } }, "multiPlayer": { "type": "object", "properties": { "mode": { "enum": ["LiveXBox","FortNite"] }, "signUp": { "type": "object", "properties": { "url": { "type": "string" } } } } }, "console": { "type": "object", "properties": { "mode": { "enum": ["single", "multi"] }, "required": { "type": "boolean" } } } } }
不符合预期的验证通过JSON
//This product has properties it shouldn't have // signUp should be only allowed in multiplayer etc. { "type": "product", "productId": "wwwwww", "productInfo": { "type": "digital", "gameMode": { "mode": "mp3", "required": true, "signUp": "https://mmmmmmm.com", "warranty": "yes" } } }
正确示例
音频播放器(仅自身属性)
// Audio Player properties only { "type": "product", "productId": "audio", "productInfo": { "type": "digital", "gameMode": { "mode": "mp3" // or "wav" } } }
控制器(仅自身属性)
// Controller properties only { "type": "product", "productId": "controller", "productInfo": { "type":"digital", "gameMode": { "mode": "Retro", // or XboxElite "batteryLevel": "high", "warranty": "yes", "tradeIn": true } } }
问题根源
- 所有产品定义(
$defs下的schema)未设置additionalProperties: false,导致额外属性不会被拦截; audioPlayer的定义存在语法错误,直接将gameMode作为顶级字段,而非放在properties下,导致该规则完全无效。
修正后的Schema
{ "type": "object", "required": ["type", "productId"], "additionalProperties": false, "properties": { "productId": { "type": "string", "minLength": 1 }, "type": { "enum": ["product"] }, "productInfo": { "type": "object", "additionalProperties": false, "required": ["type", "gameMode"], "properties": { "type": { "enum": ["digital"] }, "gameMode": { "type": "object", "oneOf": [ {"$ref": "#/$defs/audioPlayer"}, {"$ref": "#/$defs/multiPlayer"}, {"$ref": "#/$defs/console"}, {"$ref": "#/$defs/controller"} ] } } } }, "$defs": { "audioPlayer": { "type": "object", "additionalProperties": false, "required": ["mode"], "properties": { "mode": { "enum": ["mp3", "wav"] } } }, "controller": { "type": "object", "additionalProperties": false, "required": ["mode"], "properties": { "mode": { "enum": ["Retro", "XboxElite"] }, "batteryLevel": {"type": "string"}, "warranty": {"type": "string"}, "tradeIn": {"type": "boolean"} } }, "multiPlayer": { "type": "object", "additionalProperties": false, "required": ["mode"], "properties": { "mode": { "enum": ["LiveXBox", "FortNite"] }, "signUp": { "type": "object", "additionalProperties": false, "properties": { "url": {"type": "string"} } } } }, "console": { "type": "object", "additionalProperties": false, "required": ["mode"], "properties": { "mode": { "enum": ["single", "multi"] }, "required": {"type": "boolean"} } } } }
修正说明
- 给每个产品的schema添加
additionalProperties: false,严格限制仅允许自身定义的属性; - 修正
audioPlayer的结构,将原错误的gameMode字段改为mode并放入properties下,和其他产品字段名统一; - 为每个产品的
mode字段添加required约束,确保产品类型标识明确; - 给
multiPlayer下的signUp也添加additionalProperties: false,进一步约束嵌套对象的属性。
验证效果
修正后的Schema会拦截之前的错误示例(因包含非对应产品的required、signUp、warranty属性),而正确示例则能正常通过验证。
内容的提问来源于stack exchange,提问作者BreenDeen
相关产品推荐
相关产品推荐

