nlohmann/json最省心:头文件即用、utf-8原生、异常提示明确;rapidjson适合高性能/嵌入式,需手动内存管理;jsoncpp易踩坑,应启用strictmode并避免隐式创建字段。

用 nlohmann/json 解析 JSON 字符串最省心
绝大多数 C++ 项目现在首选 nlohmann/json,它头文件即用、无依赖、语法接近 Python,且支持 UTF-8 原生处理。解析失败时会抛出 json::parse_error 异常,不是静默失败。
常见错误:直接用 json::parse() 解析含 BOM 或非法空格的字符串,导致 parse_error;或没加 try/catch 就调用,程序崩溃。
- 确保输入是合法 UTF-8 字符串(
std::string或std::vector<uint8_t></uint8_t>) - 用
try { auto j = json::parse(s); }包裹解析逻辑 - 访问字段前先用
j.contains("key")或j.is_object()检查结构 - 若 JSON 来自网络,建议用
json::parse(s, nullptr, false)关闭异常(第三参数为false),改用返回json::value_t::discarded判断失败
用 rapidjson 做高性能解析或嵌入式场景
rapidjson 是唯一真正兼顾解析速度和内存可控性的选择,尤其适合高频解析、资源受限环境。但它不自动管理内存——Document 需显式分配 MemoryPoolAllocator,且字符串视图(Value::GetString())生命周期绑定于 Document 实例。
典型坑:把 Value::GetString() 结果存成 std::string 以外的裸指针,后续 Document 析构后访问就崩;或忘记调用 Parse() 后检查 IsObject()/HasMember() 就直接取值。
立即学习“C++免费学习笔记(深入)”;
- 用
Document d; d.Parse(json_str.c_str());后必须检查d.HasParseError() == false - 取字符串推荐立刻转成
std::string{v.GetString(), v.GetStringLength()},避免悬垂指针 - 批量解析时复用同一个
Document对象,调用d.ParseInsitu()可减少拷贝(需传入可写缓冲区) - 不依赖 STL 的场合(如裸机 firmware),启用
RAPIDJSON_HAS_STDSTRING宏要谨慎,可能引入隐式依赖
别踩 jsoncpp 的坑:过时 API 和默认不安全
jsoncpp 在旧项目中仍常见,但它的 Json::Reader 默认不校验 UTF-8,遇到乱码 JSON 会静默截断;且 root["key"] 访问不存在字段时自动创建空对象,容易掩盖逻辑错误。
更麻烦的是:1.9.x 版本开始强制要求用 JSONCPP_NODISCARD,但很多老代码没适配,编译警告一堆;Value::asInt() 对浮点数字符串(如 "123.0")直接返回 0 而非报错。
- 初始化
Reader时务必传入Json::Features::strictMode() - 取值一律用
root.get("key", Json::Value()).isString()先判类型再取,别信root["key"].asString() - 升级到 1.9+ 后,所有
get()调用需检查返回值是否为Json::nullValue - 生成 JSON 时禁用
StyledWriter(已废弃),改用StreamWriterBuilder
解析后如何安全访问嵌套字段?统一用“路径式”校验
无论用哪个库,深层嵌套(如 data.user.profile.avatar.url)手动逐层 .contains() 或 .IsObject() 太啰嗦,也容易漏检。推荐封装一个通用路径访问函数,内部做短路判断。
例如在 nlohmann/json 中可写:
template<typename T>
std::optional<T> get_nested(const json& j, const std::vector<std::string>& path) {
const json* cur = &j;
for (const auto& key : path) {
if (!cur->is_object() || !cur->contains(key)) return std::nullopt;
cur = &(*cur)[key];
}
return cur->get<T>();
}
// 用法:auto url = get_nested<std::string>(j, {"data","user","profile","avatar","url"});
这个模式比硬写 j["data"]["user"]["profile"]["avatar"]["url"].get<string>()</string> 安全得多——后者任意一级缺失都会抛异常或返回空字符串,根本分不清是字段不存在还是值为空。
真正复杂的地方不在选库,而在于:JSON 结构是否可信、字段是否必填、空值如何语义化。这些必须靠路径校验 + 显式缺省策略控制,不能交给库默认行为兜底。











