JSON Schema中$dynamicRef与$dynamicAnchor的作用、原理及与$ref、$anchor的区别
$dynamicRef 与 $dynamicAnchor 核心概念及运行机制
1. 与普通$ref/$anchor的差异
普通$ref和$anchor是静态解析的:
- 解析时机是Schema加载阶段,和实际运行时的校验上下文无关
$ref只会在它当前所在的Schema的作用域内查找匹配的$anchor,无论这个Schema被谁引用、作为哪个根Schema的子模块,引用指向的位置永远固定。
$dynamicRef和$dynamicAnchor是运行时动态解析的:
- 解析时机是实际执行数据校验的阶段,会根据当前使用的根Schema上下文调整指向
- 同一个
$dynamicAnchor名称可以在多个Schema中声明,支持外层覆盖。
2. $dynamicRef的运行规则
当解析$dynamicRef: "#锚点名"时,执行顺序如下:
- 第一步:从当前校验任务使用的根Schema开始查找是否有匹配名称的
$dynamicAnchor,如果找到就直接用根Schema的锚点对应的规则 - 第二步:如果根Schema没有匹配的锚点,再回退到
$dynamicRef所在的Schema自身作用域查找对应的$dynamicAnchor
3. 元模式示例的逻辑解读
你给出的JSON Schema官方2020-12元模式的设计,就是典型的动态引用适用场景:
基础背景
meta/core是元模式的核心基础模块,其中定义了$defs的校验规则:additionalProperties: { "$dynamicRef": "#meta" },要求$defs中的每一个子Schema都要符合#meta锚点对应的元模式规则。
两种使用场景的不同表现
- 直接用meta/core作为根Schema校验
此时根Schema就是meta/core本身,根上有$dynamicAnchor: "meta",所以$dynamicRef: "#meta"会指向meta/core自身的规则,$defs下的子Schema只会被核心元规则校验。 - 用完整的2020-12元模式作为根Schema校验
完整的根元模式通过allOf引入了meta/core,同时自身也声明了$dynamicAnchor: "meta"。此时meta/core里的$dynamicRef: "#meta"会优先匹配根Schema的锚点,也就是用包含所有校验词汇、格式词汇等完整能力的元规则来校验$defs下的子Schema,不需要修改meta/core的任何代码。
4. 设计的合理性
这个设计是为了实现无侵入的Schema扩展能力,尤其适配元模式这类需要灵活扩展的场景:
如果用普通的静态$ref实现,meta/core里的引用永远指向自身的锚点,如果你要做自定义元模式(比如新增自定义校验关键字、扩展词汇表),就必须修改meta/core模块的所有相关引用代码,复用成本极高。用动态引用后,你只需要在自定义的元模式根节点声明同名的$dynamicAnchor: "meta",底层所有复用模块的动态引用都会自动适配你扩展后的规则,不需要修改原有底层代码。
内容的提问来源于stack exchange,提问作者Andreas H.
相关产品推荐
相关产品推荐

