Go JSONPatch 怎样把 JSON Patch 操作应用到文档
来源:17golang原创
时间:2026-10-29 12:33:18 164浏览 收藏
Go 中应用 JSON Patch,核心流程是先用 DecodePatch 解析 RFC 6902 补丁数组,再调用 Patch.Apply 把这些操作按数组顺序作用到原始 JSON 文档上。解析失败或某一步路径不满足操作条件时,应保留错误,不要把原文档误当成已更新结果。
官方地址:https://github.com/evanphx/json-patch
- 补丁是操作数组,
add、remove、replace等操作按顺序影响同一个目标文档。 DecodePatch只负责把 JSON 补丁解析成Patch,真正修改文档要调用Apply或ApplyWithOptions。- 缺失路径是否容忍、是否自动补齐 add 路径、copy 是否限制扩张,都应通过选项明确表达。
先把 JSON Patch 的输入边界分清
JSON Patch 不是一组散落的字段,而是一个 JSON 数组。数组中的每个对象至少包含 op 和 path,必要时再带 value、from 等成员。RFC 6902 定义的操作包括 add、remove、replace、move、copy 和 test;它们会按数组顺序处理,前一个操作产生的文档状态会成为后一个操作的输入。
因此,应用函数需要两份独立数据:一份是补丁数组,另一份是被修改的原始文档。路径使用 JSON Pointer 语义,例如 /profile/name 指向对象的嵌套字段,数组位置则要注意操作前后的索引变化。

用 DecodePatch 解析补丁并调用 Apply
安装当前模块路径后,最小用法是先解析补丁,再应用到原始字节。下面的示例同时替换姓名并删除年龄字段,代码中的错误处理保留了“解析错误”和“应用错误”两个边界:
package main
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch/v5"
)
func main() {
original := []byte(`{"name":"Lin","age":30,"city":"Suzhou"}`)
patchJSON := []byte(`[
{"op":"replace","path":"/name","value":"Ming"},
{"op":"remove","path":"/age"}
]`)
// 先把 JSON 数组解析为有序的 Patch,格式错误在这里返回。
patch, err := jsonpatch.DecodePatch(patchJSON)
if err != nil {
panic(fmt.Errorf("解析补丁失败: %w", err))
}
// Apply 返回新的 JSON 字节;应用阶段的路径错误必须单独处理。
modified, err := patch.Apply(original)
if err != nil {
panic(fmt.Errorf("应用补丁失败: %w", err))
}
// 这里保留 JSON 结果,业务层可以再做反序列化和字段校验。
fmt.Println(string(modified))
}
这个调用不会要求你先把原文档反序列化成 Go 结构体,适合在接口层先处理部分更新,再把结果交给后续业务模型。若结果需要格式化输出,可使用 ApplyIndent;若只想比较两个 JSON 是否结构相等,可使用库提供的 Equal,不要直接比较字节串的空格和键顺序。
用 ApplyOptions 明确容错边界
默认行为更适合发现客户端补丁错误:例如 remove 指向不存在的位置时,应用会返回错误。需要兼容不同版本文档时,可以用 ApplyWithOptions 显式配置,而不是在外层捕获错误后静默忽略:
func applyPatchWithPolicy(document, patchJSON []byte) ([]byte, error) {
// DecodePatch 负责校验补丁的 JSON 形状和操作结构。
patch, err := jsonpatch.DecodePatch(patchJSON)
if err != nil {
return nil, fmt.Errorf("解析 JSON Patch: %w", err)
}
options := jsonpatch.NewApplyOptions()
// 只有确认删除目标可选时,才允许忽略缺失的 remove 路径。
options.AllowMissingPathOnRemove = true
// 允许 add 自动创建中间路径时,要同步评估数据模型约束。
options.EnsurePathExistsOnAdd = true
// copy 的累计扩张可以设置上限,避免不受控地复制大对象。
options.AccumulatedCopySizeLimit = 1
AllowMissingPathOnRemove 适合“删除动作重复到达也不算失败”的场景;EnsurePathExistsOnAdd 则会影响缺少中间对象时的 add 行为。两者都可能放宽输入约束,生产接口仍应在应用前限制允许的路径和操作集合。

把结果接回业务对象时保留错误边界
应用完成后通常有两种接法:继续把 JSON 字节转发给下游,或反序列化为结构体做业务校验。后者不要只检查 json.Unmarshal 是否成功,因为 JSON 结构合法不代表状态满足业务规则。
| 阶段 | 建议动作 | 失败含义 |
|---|---|---|
| 解析补丁 | DecodePatch 返回错误即拒绝 | 补丁 JSON 或操作结构不合法 |
| 应用补丁 | 检查 Apply 错误 | 路径、索引或 test 条件不满足 |
| 业务转换 | 反序列化后再做字段校验 | 文档合法但不符合业务模型 |
如果补丁来自外部请求,建议将允许的根路径、操作类型和文档大小写进接口契约;JSON Patch 只描述如何修改文档,并不会替代权限校验、字段白名单或并发版本控制。
常见问题
Patch.Apply 会原地修改传入的字节吗?
调用方应把返回的字节视为新文档,并以返回值为准;不要忽略返回结果,也不要把原始输入当成已经更新。
为什么 remove 有时会返回路径错误?
默认策略要求目标路径存在。只有明确允许幂等删除时,才考虑通过 AllowMissingPathOnRemove 放宽该行为。
JSON Patch 和 JSON Merge Patch 是一回事吗?
不是。JSON Patch 是带操作和路径的数组,Merge Patch 更接近用对象描述字段合并;本题的 DecodePatch 与 Apply 针对 RFC 6902 JSON Patch。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
302 收藏
-
334 收藏
-
135 收藏
-
232 收藏
-
355 收藏
-
360 收藏
-
446 收藏
-
210 收藏
-
200 收藏
-
187 收藏
-
208 收藏
-
139 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习