登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

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 指向对象的嵌套字段,数组位置则要注意操作前后的索引变化。

JSON Patch 补丁数组、DecodePatch、Patch、Operation、JSON Pointer 路径、Apply 与目标 JSON 文档的静态结构关系说明图
图1:结构说明图,展示 JSON Patch 输入、操作对象与目标文档之间的关系,不是运行截图。

用 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 行为。两者都可能放宽输入约束,生产接口仍应在应用前限制允许的路径和操作集合。

ApplyOptions 与缺失 remove、自动补齐 add 路径、copy 大小限制和 Apply 的静态边界关系图
图2:边界说明图,展示 ApplyOptions 对路径容错与 copy 扩张的配置关系,不是运行截图。

把结果接回业务对象时保留错误边界

应用完成后通常有两种接法:继续把 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。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>