jsonschema - Json Schema 中 $dynamicRef $dynamicAnchor 的解释(相对于 $ref 和 $anchor)
问题描述
有人可以解释JSON Schema 中 $dynamicRef 关键字的用途吗
有关实际使用,请参阅 JSON Schema 元模式本身。
它利用了 $dynamicAnchor 和 $dynamicRef。
核心架构看起来像这样
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://json-schema.org/draft/2020-12/schema",
---- <snip>
"$dynamicAnchor": "meta", <-- first usage here
---- <snip>
"allOf": [
{"$ref": "meta/core"},
---- <snip>
]
---- <snip>
}
meta/core
(allOf
看起来像这样“包含”
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://json-schema.org/draft/2020-12/meta/core",
---- <snip>
"$dynamicAnchor": "meta", <-- second usage here
---- <snip>
"properties": {
---- <snip>
"$defs": {
"type": "object",
"additionalProperties": { "$dynamicRef": "#meta" } <-- reference here
---- <snip>
}
}
}
为什么这么复杂?它是如何工作的?即使我阅读了规范,我也无法真正理解它。
解决方案
当扩展模式可能需要覆盖引用将解析到的位置时,使用动态引用。这在扩展递归模式(如元模式)中最常见,但也有其他用途。
这可能还没有意义,所以让我们从一个例子开始,说明为什么存在动态引用。此模式描述了一个递归树结构,其值为字符串。
{
"$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
约束仅适用于树的根。in 中的递归引用/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
您可能还听说过$recursiveRef
JSON Schema 2019-09。这是动态引用的前一个化身,仅对扩展本示例中的递归模式有用。与递归引用不同,动态引用允许您在模式中设置多个扩展点。让我们将我们的示例更进一步,看看为什么它是有用的。
假设我们想要一个描述树的模式,但我们希望扩展模式能够覆盖树的叶子的模式。例如,我们可能想要一棵具有多个叶节点而不是字符串的树。我们可以使用动态引用来允许覆盖叶子。
{
"$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"
}
}
动态引用还有一些更复杂的地方,我现在不会讨论。希望这足以说明为什么存在这种复杂的机制以及何时想要使用它。我希望它也让它看起来不那么复杂。
推荐阅读
- python - 如何使用 pandas to_csv 正确翻译 \n?
- javascript - 使用 Javascript 或 Selenium Java 在事件监听器中获取绑定元素
- r - 预测函数是否使用其新预测更新历史(训练)集?
- android - 如何将 Google Play 发布前报告与 expo / react-native 结合使用
- php - 如何从codeigniter活动记录中获取result_array列表到row_array
- python - 如何将文件路径中的部分目录名称附加到 bash 或 python 中的文件名称?
- python - 将简单类序列化为文件中的一行
- android - 签名的 APK 不起作用,但调试版本工作正常
- swift - 将 Json 字符串解码为类对象 Swift
- vue.js - Vue.js 更改数据和重新渲染时间?