如何用anyOf定义两组patternProperties并禁用额外属性?
尝试使用anyOf定义两组patternProperties,期望排除所有不符合任一模式的属性,但在为这两组patternProperties设置additionalProperties: false后,出现了属性无匹配的错误。
示例JSON数据:
{ "fruitbasket": { "apple": { "color": "green" }, "pear": { "color": "green" }, "banana": { "color": "yellow" }, "apple_wrapper": { "material": "paper" }, "pear_wrapper": { "material": "cloth" }, "banana_wrapper": { "material": "none" } } }
使用的Schema:
{ "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/responseobject", "definitions": { "responseobject": { "type": "object", "properties": { "fruitbasket": { "anyOf": [ { "$ref": "#/definitions/fruit" }, { "$ref": "#/definitions/wrapper" } ] } }, "additionalProperties": false }, "fruit": { "type": "object", "patternProperties": { "^(apple|pear|banana)$": { "type": "object", "properties": { "color": { "type": "string", "enum": [ "green", "yellow", "red" ] } }, "required": [ "color" ], "additionalProperties": false } }, "additionalProperties": false }, "wrapper": { "type": "object", "patternProperties": { "^(apple|pear|banana)_wrapper$": { "properties": { "material": { "type": "string", "enum": [ "paper", "cloth", "none" ] } }, "required": [ "material" ], "additionalProperties": false } }, "additionalProperties": false } } }
当前现象:移除任意一组patternProperties旁的"additionalProperties": false,Schema即可通过验证。请问JSON Schema是否支持这种严格性?该如何实现仅允许两组patternProperties且无其他属性?
问题原因
anyOf的逻辑是实例只需满足其中一个子Schema即可。你的fruit Schema要求所有属性都匹配^(apple|pear|banana)$模式,wrapper Schema要求所有属性都匹配^(apple|pear|banana)_wrapper$模式,但你的JSON实例同时包含两类属性,所以单独用fruit或wrapper验证时,另一类属性会被各自的additionalProperties: false判定为非法的额外属性,导致anyOf的两个分支都验证失败。
解决方案
JSON Schema完全支持这种严格校验需求,最直接的实现方式是合并两组patternProperties到同一个对象Schema中,然后设置additionalProperties: false,这样就能同时允许两类合法属性,且拒绝任何不符合模式的属性:
修改后的Schema:
{ "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/responseobject", "definitions": { "responseobject": { "type": "object", "properties": { "fruitbasket": { "$ref": "#/definitions/fruitAndWrapper" } }, "additionalProperties": false }, "fruitAndWrapper": { "type": "object", "patternProperties": { "^(apple|pear|banana)$": { "type": "object", "properties": { "color": { "type": "string", "enum": ["green", "yellow", "red"] } }, "required": ["color"], "additionalProperties": false }, "^(apple|pear|banana)_wrapper$": { "type": "object", "properties": { "material": { "type": "string", "enum": ["paper", "cloth", "none"] } }, "required": ["material"], "additionalProperties": false } }, "additionalProperties": false } } }
备选方案(若需保留anyOf结构)
如果后续有扩展多组规则的需求,必须使用anyOf,可以调整为:不在anyOf的子分支中设置additionalProperties: false,而是在外部通过not规则排除所有不匹配任一模式的属性:
{ "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/responseobject", "definitions": { "responseobject": { "type": "object", "properties": { "fruitbasket": { "allOf": [ { "anyOf": [ { "$ref": "#/definitions/fruit" }, { "$ref": "#/definitions/wrapper" } ] }, { "not": { "patternProperties": { "^(?!(apple|pear|banana)(_wrapper)?$).*$": { "type": "object" } } } } ] } }, "additionalProperties": false }, "fruit": { "type": "object", "patternProperties": { "^(apple|pear|banana)$": { "type": "object", "properties": { "color": { "type": "string", "enum": ["green", "yellow", "red"] } }, "required": ["color"], "additionalProperties": false } } }, "wrapper": { "type": "object", "patternProperties": { "^(apple|pear|banana)_wrapper$": { "type": "object", "properties": { "material": { "type": "string", "enum": ["paper", "cloth", "none"] } }, "required": ["material"], "additionalProperties": false } } } } }
这种方式通过allOf组合anyOf(允许匹配任意一类属性)和not(禁止匹配不到任一模式的属性),达到同样的严格校验效果,但复杂度更高,仅推荐在有特殊扩展需求时使用。
内容的提问来源于stack exchange,提问作者JohnRDOrazio

