Python JSON Schema中allOf结合$ref验证失效问题
问题情况
我为Resource字典编写了JSON Schema,在$defs中定义PartialResource并通过allOf字段引用该定义。使用datamodel-codegen可正确生成对应的两个TypedDict类,但使用jsonschema的Draft202012Validator验证符合结构的数据时,却抛出ValidationError,提示PartialResource中的resource_kwargs、resource_path等属性为不允许的额外属性,仿佛未识别到PartialResource的定义。
相关代码与数据
JSON Schema代码
{ "title": "resource", "type": "object", "allOf": [ { "$ref": "#/$defs/PartialResource" } ], "$defs": { "PartialResource": { "title": "partial_resource", "properties": { "spec": { "title": "spec", "type": "string" }, "resource_kwargs": { "title": "resource_kwargs", "type": "object" }, "resource_path": { "title": "resource_path", "type": "string" }, "root": { "title": "root", "type": "string" }, "uid": { "title": "uid", "type": "string" } }, "required": [ "resource_kwargs", "resource_path", "root", "spec", "uid" ] } }, "properties": { "path_semantics": { "title": "path_semantics", "type": "string", "enum": [ "posix", "windows" ] }, "run_start": { "title": "run_start", "type": "string" } }, "additionalProperties": false }
生成的TypedDict代码
from typing import TypedDict, Dict, Any, NotRequired, Literal class PartialResource(TypedDict): spec: str resource_kwargs: Dict[str, Any] resource_path: str root: str uid: str class Resource(PartialResource): path_semantics: NotRequired[Literal["posix", "windows"]] run_start: NotRequired[str]
待验证数据
{ 'path_semantics': 'posix', 'uid': 'e0fc15fc-3dd3-4822-848c-76f84585202d', 'spec': 'DUMMY', 'root': '/placeholder/path', 'resource_kwargs': {'a': 1, 'b': 2}, 'resource_path': 'stack.tiff', 'run_start': '8172473c-fec5-4baf-b28a-73bc4ada3bc5' }
错误信息
jsonschema.exceptions.ValidationError: Additional properties are not allowed ('resource_kwargs', 'resource_path', 'root', 'spec', 'uid' were unexpected)
问题原因与解决方案
原因
additionalProperties: false的作用范围仅局限于当前Schema的properties字段,不会考虑allOf引用的子Schema中定义的属性。因此验证器会把PartialResource里的属性判定为额外属性。
解决方案
使用unevaluatedProperties: false替代additionalProperties: false(JSON Schema Draft 2019-09及以上版本支持)。unevaluatedProperties会检查所有未被当前Schema或任何组合关键字(如allOf)引用的Schema评估过的属性,这样就能认可PartialResource中定义的属性。
修改后的Schema如下:
{ "title": "resource", "type": "object", "allOf": [ { "$ref": "#/$defs/PartialResource" } ], "$defs": { "PartialResource": { "title": "partial_resource", "properties": { "spec": { "title": "spec", "type": "string" }, "resource_kwargs": { "title": "resource_kwargs", "type": "object" }, "resource_path": { "title": "resource_path", "type": "string" }, "root": { "title": "root", "type": "string" }, "uid": { "title": "uid", "type": "string" } }, "required": ["resource_kwargs", "resource_path", "root", "spec", "uid"] } }, "properties": { "path_semantics": { "title": "path_semantics", "type": "string", "enum": ["posix", "windows"] }, "run_start": { "title": "run_start", "type": "string" } }, "unevaluatedProperties": false }
另一种可选方案:将additionalProperties: false移到PartialResource定义中,主Schema不设置该字段,但这种方式无法限制主Schema中出现未定义的额外属性,需根据实际需求选择。
内容的提问来源于stack exchange,提问作者Eva Lott

