Python-jsonschema 中的 JSON Schema 引用机制详解

Python-jsonschema 中的 JSON Schema 引用机制详解

jsonschema An implementation of the JSON Schema specification for Python jsonschema 项目地址: https://gitcode.com/gh_mirrors/js/jsonschema

理解 JSON Schema 引用机制

在 JSON Schema 规范中,$ref$dynamicRef 关键字允许模式作者组合多个模式(或子模式)以实现复用或去重。这种引用机制是构建复杂、模块化 JSON Schema 的基础。

引用机制的核心组件

Python-jsonschema 项目使用独立的 referencing 库来实现引用解析功能,这个库提供了三个核心对象:

  1. Registry:表示一组不可变的 JSON Schema 集合(可以是内存中的或可检索的)
  2. Specification:表示 JSON Schema 规范的特定版本(不同版本可能有不同的引用行为)
  3. Resource:表示特定的 JSON Schema(通常是 Python 字典)及其关联的 Specification

资源(Resource)的重要性

一个 JSON Schema 可能被不同版本的规范解释。例如,简单的模式 {"type": "integer"}

  • 在 Draft 2020-12 下,2.0 被视为整数
  • 在 Draft 4 下,2.0 可能不被视为整数

因此,创建 Resource 时需要明确指定其适用的规范版本:

from referencing import Resource
from referencing.jsonschema import DRAFT202012

# 为 Draft 2020-12 创建资源
resource = Resource(contents={"type": "integer"}, specification=DRAFT202012)

配置引用行为的实践指南

配置 jsonschema 的引用行为主要分为两步:

  1. 创建符合需求的 referencing.Registry 对象
  2. 在实例化 Validator 时传递该 Registry

场景一:添加内存中的模式

这是最常见的场景,我们需要让一些内存中的模式可用于验证过程。

from referencing import Registry, Resource

# 定义非负整数模式
schema = Resource.from_contents({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "integer",
    "minimum": 0
})

# 创建 Registry 并添加资源
registry = Registry().with_resources([
    ("http://example.com/nonneg-int-schema", schema),
    ("urn:nonneg-integer-schema", schema),
])

# 使用自定义 Registry 创建验证器
from jsonschema import Draft202012Validator
validator = Draft202012Validator(
    {
        "type": "object",
        "additionalProperties": {"$ref": "urn:nonneg-integer-schema"},
    },
    registry=registry,
)

场景二:从文件系统解析引用

如果需要动态从文件系统加载模式,可以配置一个检索函数:

from pathlib import Path
import json
from referencing import Registry, Resource
from referencing.exceptions import NoSuchResource

SCHEMAS = Path("/tmp/schemas")

def retrieve_from_filesystem(uri: str):
    if not uri.startswith("http://localhost/"):
        raise NoSuchResource(ref=uri)
    path = SCHEMAS / Path(uri.removeprefix("http://localhost/"))
    contents = json.loads(path.read_text())
    return Resource.from_contents(contents)

registry = Registry(retrieve=retrieve_from_filesystem)

场景三:支持 YAML 模式

通过自定义检索函数,我们可以支持 YAML 格式的模式:

from pathlib import Path
import yaml
from referencing import Registry, Resource
from referencing.exceptions import NoSuchResource

SCHEMAS = Path("/tmp/yaml-schemas")

def retrieve_yaml(uri: str):
    if not uri.startswith("http://localhost/"):
        raise NoSuchResource(ref=uri)
    path = SCHEMAS / Path(uri.removeprefix("http://localhost/"))
    contents = yaml.safe_load(path.read_text())
    return Resource.from_contents(contents)

registry = Registry(retrieve=retrieve_yaml)

注意:YAML 不是所有特性都兼容 JSON 数据模型,应确保使用的 YAML 子集可以无损转换为 JSON。

场景四:通过 HTTP 自动检索资源

虽然 JSON Schema 规范不鼓励自动网络检索,但在受控环境下可以实现:

from referencing import Registry, Resource
import httpx

def retrieve_via_httpx(uri: str):
    response = httpx.get(uri)
    return Resource.from_contents(response.json())

registry = Registry(retrieve=retrieve_via_httpx)

安全警告:这种配置有潜在安全风险,应确保理解并接受其影响。

从旧版 RefResolver 迁移

旧版 jsonschema 使用 _RefResolver 进行引用解析,现已弃用。迁移指南:

store 参数替代

旧代码:

from jsonschema import Draft202012Validator, RefResolver
resolver = RefResolver.from_schema(
    schema={"title": "my schema"},
    store={"http://example.com": {"type": "integer"}},
)

新代码:

from referencing import Registry
from referencing.jsonschema import DRAFT202012

registry = Registry().with_resource(
    "http://example.com",
    DRAFT202012.create_resource({"type": "integer"}),
)

处理程序替代

旧版的 handlers 功能可以通过自定义 retrieve 函数实现:

from urllib.parse import urlsplit

def retrieve(uri: str):
    parsed = urlsplit(uri)
    if parsed.scheme == "file":
        # 处理文件协议
        ...
    elif parsed.scheme == "custom":
        # 处理自定义协议
        ...

最佳实践建议

  1. 明确指定 $schema:在模式中使用 $schema 关键字消除版本歧义
  2. 优先使用内存模式:对于固定模式,预加载到内存更高效
  3. 谨慎使用网络检索:仅在受控环境中使用自动网络检索
  4. 考虑性能影响:频繁的文件系统或网络访问会影响验证性能
  5. 测试边缘情况:特别是跨版本引用和循环引用情况

通过合理配置引用机制,可以构建出既灵活又强大的 JSON Schema 验证系统。

jsonschema An implementation of the JSON Schema specification for Python jsonschema 项目地址: https://gitcode.com/gh_mirrors/js/jsonschema

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈昂钧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值