nlohmann/json解析JSON最省心,因其头文件直连、无依赖、支持隐式转换;但需注意异常捕获、字段存在性检查、BOM处理、结构体序列化手动定义及性能优化。

用 nlohmann/json 解析 JSON 字符串最省心
绝大多数 C++ 项目现在首选 nlohmann/json,不是因为它“最标准”,而是它把「写起来不反人类」做到了底。头文件直连、无依赖、支持隐式类型转换,连 std::vector 和 std::map 都能直接赋值。
常见错误现象:手动写 parse() 却没捕获 json::parse_error,程序一遇到非法 JSON 就崩;或者把 json j = ... 当成普通对象,误以为支持 j["key"].c_str() —— 实际得用 j["key"].get<:string>()</:string> 或 j["key"].dump()。
- 安装只要拷贝单个
json.hpp头文件,或用 vcpkg:vcpkg install nlohmann-json - 解析失败必须 try/catch:
try { auto j = json::parse(json_str); } catch (const json::parse_error& e) { /* 打印 e.what() */ } - 访问嵌套字段前务必检查存在性:
if (j.contains("data") && j["data"].is_object()) { ... },否则越界抛异常 -
j["missing_key"]不报错,但返回一个空的json对象(j["missing_key"].is_null() == true),容易埋雷
从文件读 JSON 时别忘了编码和换行问题
Windows 上用 std::ifstream 默认以文本模式打开,可能把 \r\n 自动转成 \n,对 JSON 影响不大;但若文件含 UTF-8 BOM,nlohmann/json 会直接报 [json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal。
- 读文件推荐用二进制模式 + 手动转 string:
std::ifstream f("config.json", std::ios::binary); f.seekg(0, std::ios::end); size_t size = f.tellg(); f.seekg(0); std::string buf(size, ' '); f.read(&buf[0], size); auto j = json::parse(buf); - 如果确定是 UTF-8 且带 BOM,先跳过前 3 字节:
if (buf.size() >= 3 && (uint8_t)buf[0] == 0xEF && (uint8_t)buf[1] == 0xBB && (uint8_t)buf[2] == 0xBF) buf = buf.substr(3); - Linux/macOS 下一般没问题,但 CI 环境若用容器运行,注意挂载的文件系统是否保留原始换行符
序列化结构体要自己写 to_json/from_json
nlohmann/json 不自动反射,想把 struct User { std::string name; int age; }; 直接转成 JSON,必须显式定义两个函数。漏写任意一个,编译报错信息极不友好:no matching function for call to ‘to_json(...)。
立即学习“C++免费学习笔记(深入)”;
- 定义必须在命名空间
nlohmann内,且参数类型要严格匹配:void to_json(json& j, const User& u) { j = json{{"name", u.name}, {"age", u.age}}; } -
from_json要做字段存在性检查:if (j.contains("name")) u.name = j["name"].get<:string>();</:string>,否则缺失字段会触发默认构造(比如std::string变空) - 如果结构体字段名和 JSON key 不一致,别硬改字段名,用
json::object_t中转更安全
性能敏感场景慎用 nlohmann/json 的动态类型
它内部用 std::map 存 object、std::vector 存 array,解析后所有数据都在堆上分配。千级以下 JSON 没问题;但若每秒解析上万条日志 JSON,parse() + dump() 组合容易成为瓶颈,valgrind --tool=massif 一看堆分配就清楚。
- 高频解析建议预分配 buffer,避免反复 new/delete:
json j; j.parse(json_str.c_str(), json_str.c_str() + json_str.size()); - 只读场景可用
simdjson(需 C++17),解析速度通常快 3–5 倍,但 API 更底层,要手动处理ondemand::parser和ondemand::document - 嵌入式或内存受限环境,考虑
ArduinoJson(6.x 版本),但它的StaticJsonDocument大小必须编译期确定,算错就溢出
真正麻烦的从来不是“怎么解析”,而是“字段名拼错没报错”“空值被当成默认值”“BOM 导致本地能跑线上挂掉”——这些细节不写进日志,调试时基本靠猜。










