C++ JSON库不支持裸指针直接操作,必须用值语义对象或智能指针封装;nlohmann/json等库内部用unique_ptr管理内存,保存Value*易致悬空,正确做法是用栈对象持有根节点或JSON Pointer安全定位字段。

JSON解析库不支持裸指针直接操作,必须用智能指针或容器封装
C++标准库不带JSON解析能力,所有主流方案(如 nlohmann/json、jsoncpp、rapidjson)都把解析结果封装成值语义对象,不是裸指针。试图用 json* 持有解析结果,要么编译失败,要么引发双重释放——因为这些库内部已用 std::unique_ptr 或引用计数管理内存。
常见错误现象:json* j = new json(...); delete j; 看似可行,但后续调用 j->at("key") 可能崩溃,因为 nlohmann::json 的子节点访问返回的是临时 json 值,不是指针;而 rapidjson::Value* 虽可取地址,但该指针生命周期绑定于 Document 对象,Document 析构后悬空。
- 正确做法:用
json(nlohmann)或rapidjson::Document栈对象持有根节点,所有子节点通过[]、at()、operator[]获取引用或值拷贝 - 若需“指针语义”,优先用
std::shared_ptr<json>包裹整个结构,而非对字段逐个 new -
jsoncpp的Json::Value*仅在手动调用new Json::Value时存在,但官方示例从不推荐——它不自动管理子树内存,极易泄漏
nlohmann/json 中的“指针式”访问:用 json::pointer 而非 C++ 指针
真正接近“指针操作”的是 JSON Pointer(RFC 6901),nlohmann 提供 json::pointer 类型和 at(json::pointer) 方法,它用字符串路径定位嵌套字段,比手写多层 ["a"]["b"]["c"] 更安全、可拼接、可复用。
使用场景:配置文件中动态路径读取、API响应字段名由上游约定、需要统一错误处理的深层字段提取。
立即学习“C++免费学习笔记(深入)”;
-
j.at("/data/items/0/name"_json_pointer)—— 注意字符串前缀/和_json_pointer字面量运算符 - 路径非法时抛
json::out_of_range,不是返回空指针,所以必须 try/catch 或用value()提供默认值 - 不支持通配符或正则,
/items/*/id会报错;数组索引必须是数字字符串,"0"合法,0不合法
rapidjson 中 Value* 的生命周期陷阱
rapidjson::Value* 确实可获取,但它的有效性完全依赖于其所属的 Document 或 Value 对象未被移动、析构或重新赋值。这是最常踩的坑:把 doc["config"].GetObject()["timeout"].GetInt() 中间某步的 Value* 存下来,后续调用时 doc 已被 move 走,指针立刻失效。
- 绝对不要保存
Value*超过单次表达式作用域;若需跨函数传递,传const rapidjson::Value&或整个Document& -
Document解析后不能 resize 内存池(如再调用Parse()),否则原有Value*全部作废 - 想“借用”某个子节点长期使用?只能深拷贝:
rapidjson::Value v; v.CopyFrom(src, doc.GetAllocator());,否则就是悬空指针
什么时候真该用裸指针?几乎从不
唯一合理场景是对接 C 风格 API(比如某个 SDK 要求传 const char** 输出 key 列表),此时需把 nlohmann::json 的键转为 C 字符串数组,临时分配内存并交由外部释放。但这属于胶水代码,不是 JSON 处理主逻辑。
其他所谓“需要指针”的需求,本质是误解了 C++ JSON 库的设计哲学:它们用值语义规避所有权争议,用引用避免拷贝开销,用异常替代空指针检查。强行塞裸指针进去,只会让代码更脆弱、更难调试。
容易被忽略的一点:json::dump() 返回 std::string,不是 char*;若你还在 c_str() 后存指针,字符串临时对象一销毁,指针就指向垃圾内存。


















