JSON Schema draft 2020如何从外部Schema导入$defs定义节
JSON Schema draft 2020-12 跨Schema复用$defs问题解答
方案不生效的根本原因
之前测试的几种写法报错、不符合预期,核心是两个规则理解偏差:
$ref不存在「拉取引用目标内容和当前节点合并」的语义。无论写在Schema的哪个位置,$ref的作用都是标记当前节点的校验逻辑跳转到引用目标执行。Draft 2020-12虽然允许$ref和同级关键字共存,但两者是并列校验关系:即实例既要满足$ref指向的Schema规则,也要满足同级其他关键字的规则,不会自动把两边的$defs、properties等对象的键值合并到一起。之前感知到properties“正常合并”,本质是两边的properties规则同时生效,不是内容被拼接在了一起。$defs是规范定义的非校验类复用容器,它的值必须是对象,且对象的每个成员值必须是合法Schema(对象/布尔类型)。之前写的"$defs": {"$ref": "base#/$defs"}报错,是因为$defs下直接放了值为字符串的$ref字段,不符合「$defs每个成员都是Schema」的要求;写allOf数组报错同理,allOf是Schema校验关键字,不能直接作为$defs的成员名存在,$defs下的每个键对应一个独立的可复用Schema片段,不是写组合校验逻辑的位置。
可落地的实现方式
不需要复制粘贴base中的$defs内容,只需要在better的$defs节点下,为需要暴露给下游的定义创建引用别名即可,这是所有兼容Draft 2020-12的校验器(包括AJV)都支持的标准写法。
修改后的better Schema
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://example.com/schemas/better", "$ref": "https://example.com/schemas/base", "$defs": { "abc": { "$ref": "base#/$defs/abc" } }, "type": "object", "properties": { "betterprop": { "title": "an additional, even better property!", "$ref": "#/$defs/abc" } } }
如果base的$defs下有多个需要暴露的定义,逐个在better的$defs下加对应键的$ref映射即可,不需要复制定义的实际内容,所有别名都是轻量的引用跳转,不会产生冗余。
对应best Schema的引用方式
修改完成后,best里就可以直接通过better#/$defs/abc路径访问对应定义,不需要再关联base的路径:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://example.com/schemas/best", "$ref": "https://example.com/schemas/better", "type": "object", "properties": { "bestprop": { "title": "the best property!", "$ref": "better#/$defs/abc" } } }
补充说明
JSON Schema的引用解析是基于$id的全局寻址逻辑,只要校验器加载过对应Schema,任何位置都可以直接通过完整URI引用目标节点,不存在“必须先导入才能用”的限制。这个方案本质是在better层做了一层路径别名转发,完全符合规范要求,也不会破坏原有base、better的校验逻辑。
目前规范没有提供批量导入整个$defs节点的能力,因为$defs本身是存储容器不是可被引用的Schema节点,且规范没有定义对象键值合并的相关逻辑,所有引用都是节点级别的跳转,不存在批量导入键值对的语法。如果需要复用大量定义,逐个配置别名是目前最稳定、兼容性最好的方案。
内容的提问来源于stack exchange,提问作者Jason C
相关产品推荐
相关产品推荐

