使用 json::patch() 进行局部更新需传入符合 rfc 6902 的 json patch 数组,path 必须为合法 json pointer(如 "/name"),特殊字符 / 和 ~ 需转义为 ~1 和 ~0,原数据与 patch 均须先 parse。

怎么用 json_patch 做局部更新而不破坏原结构
直接调用 json::patch() 是最简方式,但它要求 patch 文档本身是合法的 JSON Patch 格式数组(RFC 6902),不是任意对象。常见错误是传入一个普通 json 对象,结果静默失败或抛出 parse_error。
使用场景:服务端接收前端发来的部分字段更新请求(比如只改用户头像和昵称),不想反序列化整个对象再赋值。
- patch 数据必须是
json类型的数组,每个元素是带"op"、"path"、"value"的对象 -
"path"必须是合法 JSON Pointer(如"/name"、"/tags/0"),不能写成"name"或"$.name" - 原数据和 patch 都要先用
json::parse()解析,不能混用字符串和json对象
auto original = nlohmann::json::parse(R"({"name":"Alice","age":30})");
auto patch = nlohmann::json::parse(R"([
{"op":"replace","path":"/name","value":"Bob"},
{"op":"add","path":"/email","value":"bob@example.com"}
])");
auto result = original.patch(patch); // 返回新 json,original 不变
json_pointer 字符串怎么写才不越界或解析失败
JSON Pointer 本质是路径表达式,不是 XPath 或 CSS 选择器。最容易踩的坑是斜杠处理和特殊字符转义——比如键名含 / 或 ~,必须分别转为 ~1 和 ~0,否则 json_pointer 构造时直接抛 parse_error。
使用场景:从嵌套很深的配置里取某个开关字段,或动态拼接 path 做条件读取。
- 根路径写
""(空字符串),不是"/";"/"表示 key 为空的对象成员 - 访问数组第 i 项用
"/items/0",不能用"/items[0]"或"/items.0" - 运行时拼接 path 要手动转义:遇到
~替换为~0,遇到/替换为~1
auto j = nlohmann::json::parse(R"({"a/b":{"c":"val"},"d~e":42})");
auto ptr1 = nlohmann::json_pointer("/a~1b/c"); // 注意 ~1
auto ptr2 = nlohmann::json_pointer("/d~0e"); // 注意 ~0
std::cout
<h3>为什么 <code>patch()</code> 后丢失类型信息或精度</h3>
<p><code>json::patch()</code> 内部会做一次深拷贝 + 合并,所有数字默认按 double 存储,整数如 <code>123</code> 可能变成 <code>123.0</code>;bool 和 null 也可能在 patch 过程中被隐式转换。这不是 bug,而是 nlohmann/json 的设计取舍:它优先保证 JSON 标准兼容性,而非 C++ 原生类型保真。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<p>性能影响:每次 <code>patch()</code> 都触发完整遍历,对超大文档(>10MB)会有明显延迟,且无法流式处理。</p>
- 如果必须保持整数精度,补丁前先用
j["field"].is_number_integer()检查,再手动赋值 - 避免对同一对象反复 patch:先 accumulate 所有操作到一个 patch 数组,再单次调用
- 不支持原子性回滚 —— 如果 patch 中间某步失败,前面已生效的操作不会撤销
如何安全地组合 json_pointer 和 patch 做条件更新
直接把 json_pointer 当字符串拼进 patch 的 "path" 字段没问题,但要注意:pointer 实例本身不校验目标是否存在,而 patch() 在执行 "remove" 或 "replace" 时,若 path 不存在会抛 out_of_range 异常。线上服务不捕获就崩。
真实需求往往是“如果字段存在就更新,不存在就跳过”,但标准 patch 没这个语义。
- 先用
contains()或at()+try/catch预检路径有效性,再决定是否生成 patch - 不要依赖
patch()的返回值判断成功与否 —— 它总返回新对象,异常才是失败信号 - 对用户可控输入的 path,务必用
json_pointer::parse()验证合法性,防止注入类似"/../password"的非法路径(虽然 JSON Pointer 规范不支持 ..,但解析器可能不严格)
复杂点在于:JSON Pointer 的“存在性”和 patch 的“可操作性”是两层检查,漏掉任何一层都可能让服务在边界 case 下挂掉。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










