github.com/evanphx/json-patch 是最成熟的 rfc 6902 兼容库,支持六种操作且严格处理路径转义;必须用 []byte 输入输出,struct 需先 marshal 再 unmarshal;add 用 /- 追加、replace 要求路径存在;缓存 patch 实例可提升重复应用性能。

用 github.com/evanphx/json-patch 库做标准 JSON Patch(RFC 6902)操作
Go 原生 encoding/json 不支持 JSON Patch,必须依赖第三方库。目前最成熟、符合 RFC 6902 的是 github.com/evanphx/json-patch —— 它能正确解析 add、remove、replace、move、copy、test 六种操作,且对数组索引、嵌套路径(如 /user/name)、转义字符(如 /a~1b)处理严谨。
实操建议:
- 用
jsonpatch.DecodePatch()加载原始 patch 数据([]byte),它会预校验语法和操作合法性;失败时返回明确错误,比如"invalid operation: invalid op 'xxx'" - 调用
patch.Apply()时传入目标文档的[]byte,不是interface{}或结构体指针——它内部会重新解码/编码,传错类型会导致静默失败或 panic - 如果 patch 中有
test操作,失败会直接返回 error,不会部分执行;想跳过 test 可先用jsonpatch.NewPatchFromBytes()+ 手动过滤
处理 Go 结构体时别直接传 struct,先序列化再 patch
很多人想“给 struct 字段打补丁”,试图把 *MyStruct 直接喂给 Apply(),结果 panic 或无效果。这个库只认 []byte,不理解 Go 类型系统。
正确流程是:struct → json.Marshal() → patch.Apply() → json.Unmarshal() 回 struct。
注意点:
- struct 字段必须导出(首字母大写),且带
json:tag,否则Marshal后字段丢失,patch 路径找不到目标 - 如果 struct 里有
time.Time、map[interface{}]interface{}这类非标准 JSON 类型,Marshal会失败,得先转换成 JSON 友好格式 - 避免反复 Marshal/Unmarshal 大文档:可缓存中间
[]byte,或用json.RawMessage延迟解析
add 和 replace 对数组索引的处理差异
JSON Patch 路径里的数组下标行为容易误判:/items/0 在 add 和 replace 下语义不同。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
典型错误现象:对空数组 [] 执行 {"op":"add","path":"/0","value":1} 报错 "unable to add to non-existent parent",但 {"op":"add","path":"/-","value":1} 就成功。
关键区别:
-
add到具体数字索引(如/0)要求父容器存在且长度 ≥ 索引值;想追加用/-(自动定位末尾) -
replace要求路径指向一个已存在的值;/0替换第一个元素,/-是非法路径(因为-不代表现有位置) - 数组越界不会静默忽略:比如对
[1,2]执行replace到/5,直接报错"index out of bounds"
性能敏感场景下避免重复解析 patch
如果同一 patch 模板要反复应用到不同文档(比如 API 网关批量更新),每次调用 jsonpatch.DecodePatch() 都会解析 JSON 并构建操作链表,开销不小。
优化方式:
- 把
jsonpatch.Patch实例缓存起来复用,只要 patch 内容不变,就只需解析一次 - 注意并发安全:
Patch.Apply()是只读操作,可多 goroutine 并发调用;但别在 patch 上做任何修改(比如改内部 slice) - 如果 patch 来自用户输入,缓存前需确认其大小(防止恶意超长 patch 占满内存),建议限制原始字节数 ≤ 1MB
路径里的波浪号 ~ 和斜杠 / 转义规则很细,一不小心就拼错 /user~1profile,这种细节不报语法错,但 patch 会静默失效。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










