如何在非根子Schema中正确实现JSON Schema递归?
如何正确设计包含子Schema递归的JSON Schema?
问题描述
我正在设计一个包含递归元素的JSON Schema,递归元素不在根节点,而是位于子Schema中。原Schema代码如下:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "http://example.org/json-schema/struct.schema.json", "type": "array", "items": { "title": "Structure", "type": "object", "properties": { "id": { "title": "ID", "type": "string" }, "label": { "title": "Label", "type": "string" }, "children": { "$anchor": "children", "title": "Children", "type": "array", "items": { "title": "Structure Item", "type": "object", "properties": { "id": { "title": "ID", "type": "string" }, "href": { "title": "Reference", "type": "string" }, "label": { "title": "Structure Item Label", "type": "string" }, "sort_label": { "title": "Sort Label", "type": "string" }, "children": {"$ref": "children"} } }, "anyOf": [ {"required": ["id", "href"]}, {"required": ["id", "children"]} ] } }, "required": ["id", "label", "children"] } }
编译该Schema时,在线验证器报错,使用python-fastjsonschema时会陷入循环。尝试过用"$ref": "#/items/properties/children"引用非命名锚点,Schema能编译但验证时报错。
问题分析
- 逻辑位置错误:原Schema中
anyOf规则被放在了children数组的Schema下,但实际这个规则是约束数组内的每个结构项(Structure Item),而非数组本身。 - 递归引用混淆:用
$anchor绑定的是children数组的Schema,但递归需要的是结构项的Schema,导致引用目标错误,进而引发循环或验证失败。 - 工具兼容性:部分JSON Schema工具对嵌套锚点的递归支持不佳,而使用
$defs定义可复用Schema是更标准、兼容性更好的方式。
正确的Schema写法
将可复用的递归结构项抽离到$defs中,明确递归引用的目标,同时修正规则的位置:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "http://example.org/json-schema/struct.schema.json", "type": "array", "items": { "title": "Structure", "type": "object", "properties": { "id": { "title": "ID", "type": "string" }, "label": { "title": "Label", "type": "string" }, "children": { "title": "Children", "type": "array", "items": {"$ref": "#/$defs/StructureItem"} } }, "required": ["id", "label", "children"] }, "$defs": { "StructureItem": { "title": "Structure Item", "type": "object", "properties": { "id": { "title": "ID", "type": "string" }, "href": { "title": "Reference", "type": "string" }, "label": { "title": "Structure Item Label", "type": "string" }, "sort_label": { "title": "Sort Label", "type": "string" }, "children": { "title": "Children", "type": "array", "items": {"$ref": "#/$defs/StructureItem"} } }, "anyOf": [ {"required": ["id", "href"]}, {"required": ["id", "children"]} ] } } }
关键调整说明
- 抽离递归结构:把
Structure Item的Schema放到$defs中,作为独立的可复用单元,递归时直接引用这个定义,避免混淆。 - 修正规则位置:将
anyOf约束移到StructureItem下,确保每个结构项都满足"带链接"或"带子节点"的要求。 - 清晰的引用路径:使用
#/$defs/StructureItem这种明确的JSON Pointer引用,兼容性更强,不会出现锚点绑定错误的问题。
内容的提问来源于stack exchange,提问作者user3758232
相关产品推荐
相关产品推荐

