功能说明
apply_patch 实现 RFC 6902 JSON Patch 的常用操作:add、remove、replace、move、copy 和 test。每个操作通过 JSON Pointer 定位目标,指针中的 ~1 解码为 /,~0 解码为 ~。函数先对输入文档做深拷贝,所有修改都发生在副本上,因此原文档保持不变,便于失败重试或审计。
add 对数组支持 - 追加和插入到指定下标,对对象则新增或替换字段;replace 要求目标已存在;move 会先删除源值再添加到目标,并拒绝把值移动到自己的子路径;test 使用类型敏感的深比较,避免 Python 中 True == 1 造成误判。出错时会附带操作序号和操作类型,便于定位补丁中的问题。
关键代码
数组下标严格校验,不接受负数、前导零或越界:
if token == "-":
raise JsonPatchError("'-' is only valid for add operations")
if not token.isdigit() or (len(token) > 1 and token.startswith("0")):
raise JsonPatchError(f"invalid array index '{token}'")
index = int(token)
upper = length if allow_end else length - 1
if index > upper:
raise JsonPatchError(f"array index {index} is out of range")
test 的深比较先比较类型,再递归比较字典键集合和列表元素。
使用注意
该实现不支持删除整个文档根,remove 空指针会抛错;replace、add 和 test 支持根路径。move 只检查目标是否为源的子路径,不检查其他操作之间的依赖顺序,补丁必须按 RFC 规定的顺序书写。copy 会深拷贝源值,因此复制大对象会增加内存开销。
add 在对象上遇到已存在的键会替换,这是 RFC 允许的行为;在数组上插入会使后续元素后移。test 的值必须与文档中的值类型完全一致,整数和浮点数在 Python 中类型不同,可能被判为不相等。补丁中的未知操作会抛错,不会静默跳过。所有错误都是 JsonPatchError,原始文档不会被部分修改。
验证方式
示例对包含名称、标签和元数据的文档应用一组补丁:替换名称、向数组追加、创建 flags 对象、复制 count、移动 active、测试 count 并删除临时字段。输出应包含 name: updated、tags: [a, b, c]、flags.active: true,而原文档仍保持 name: demo 和 meta.active: true。随后测试失败的 test 和移动到子路径的 move,应分别输出带操作序号的错误信息。