当扩展架构可能需要覆盖引用将解析到的位置时,使用动态引用。这在扩展递归模式(如元模式)中最常见,但也有其他用途。
这可能还没有意义,所以让我们从一个例子开始说明为什么存在动态引用。此模式描述了一个递归树结构,其值为字符串。
{
"$id": "https://example.com/schemas/string-tree",
"type": "array",
"items": {
"anyOf": [
{ "type": "string" },
{ "$ref": "#" }
]
}
}
这是一个针对该架构进行验证的实例。
["a", ["b", "c", ["d"], "e"]]
现在,假设我们要扩展此模式,以便每个分支最多有两个节点。您可能很想尝试以下架构。
{
"$id": "https://example.com/schemas/bounded-string-tree",
"$ref": "/schemas/string-tree",
"maxItems": 2
}
但是,maxItems 约束仅适用于树的根。 /schemas/string-tree 中的递归引用仍然指向自身,不限制节点数。
因此,["a", "b", "c"] 将无法按预期进行验证,因为存在三个节点。但是,["a", ["b", "c", "d"]] 不会失败,因为仅针对 /schemas/string-tree 架构而不是 /schemas/bounded-string-tree 架构验证了具有三个节点的分支。
因此,为了扩展递归模式,我需要一种方法来允许扩展模式 (/schemas/bound-string-tree) 更改扩展模式 (/schemas/string-tree) 中的引用目标。动态引用提供了这种机制。
{
"$id": "https://example.com/schemas/base-string-tree",
"$dynamicAnchor": "branch",
"type": "array",
"items": {
"anyOf": [
{ "type": "string" },
{ "$dynamicRef": "#branch" }
]
}
}
在这种情况下,动态引用和锚点的工作方式与常规引用和锚点相同,只是现在如果需要,可以通过扩展模式覆盖引用。如果没有扩展模式,它将转到这个动态锚点。如果有一个扩展模式声明了一个匹配的动态锚,它将覆盖这个更改动态引用解析到的位置。
{
"$id": "https://example.com/schemas/bounded-string-tree",
"$dynamicAnchor": "branch",
"$ref": "/schemas/base-string-tree",
"maxItems": 2
}
通过在/schemas/bounded-string-tree 中设置“分支”动态锚点,我们有效地覆盖了任何未来对“分支”的动态引用以解析到该位置。
现在,["a", ["b", "c", "d"]] 将无法按预期对/schema/bounded-string-tree 进行验证。
您可能还听说过 JSON Schema 2019-09 中的 $recursiveRef。这是动态引用的前一个化身,仅对扩展本示例中的递归模式有用。与递归引用不同,动态引用允许您在模式中设置多个扩展点。让我们更进一步地看看我们的示例为何有用。
假设我们想要一个描述树的模式,但我们希望扩展模式能够覆盖树的叶子的模式。例如,我们可能想要一棵具有多个叶节点而不是字符串的树。我们可以使用动态引用来允许覆盖叶子。
{
"$id": "https://example.com/schemas/base-tree",
"$dynamicAnchor": "branch",
"type": "array",
"items": {
"anyOf": [
{ "$dynamicRef": "#leaf" },
{ "$dynamicRef": "#branch" }
]
},
"$defs": {
"leaf": {
"$dynamicAnchor": "leaf",
"type": "string"
}
}
}
现在我们有两个扩展点,可以创建一个有界数树。
{
"$id": "https://example.com/schemas/bounded-number-tree",
"$dynamicAnchor": "branch",
"$ref": "/schemas/base-tree",
"maxItems": 2,
"$defs": {
"$dynamicAnchor": "leaf",
"type": "number"
}
}
动态引用还有一些更复杂的地方,我暂时不会讨论。希望这足以说明为什么存在这种复杂的机制以及何时想要使用它。我希望它也让它看起来不那么复杂。