nlohmann/json 实战:C++ JSON 解析与序列化的单头文件方案 nlohmann/json 实战C JSON 解析与序列化的单头文件方案【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json最近联调一个接口服务方返回的 JSON 嵌套了三层。你在 C 里把它读成std::string之后只能靠下标一层层剥先判断有没有server再判断timeout_ms是不是数字最后还要处理字符串转义。剥到一半对方说这个字段可能不返回——你那一串运行时判断立刻全部作废第二天另一个同事在别的项目里又把同样的代码抄了一遍。这个项目在解决什么问题nlohmann/json 是一个单头文件的 C 库把 JSON 做成了一等数据类型解析后的json值可以直接用下标、迭代器和std::cout操作。和 RapidJSON 这类偏底层、按 DOM/SAX 两层组织 API 的库相比它的关键取舍是把语法直觉和零依赖集成放在前面代价是每个节点用联合体存储内存上不算极致紧凑——这一点项目在文档里写得很直白。五分钟跑通把single_include/nlohmann/json.hpp拷进项目代码长这样#include iostream #include nlohmann/json.hpp using json nlohmann::json; int main() { // 从字符串解析失败会抛 parse_error带错误位置 json j json::parse(R({server: {timeout_ms: 5000}})); // 读键缺失时返回默认值不抛异常 int t j[server].value(timeout_ms, 3000); // 链式写入中间对象不存在会自动创建 j[server][new_endpoint] /v2; // 输出 2 空格缩进的美化版 std::cout j.dump(2) \n; return 0; }用任意支持 C11 的编译器加上这个头文件就能编译一步都不会出错。官方 README 里那个j {{pi, 3.141}, {happy, true}}的初始化列表写法也成立写起来像在拼字典。三个决定性的设计取舍联合体装下所有类型。每个json节点内部是一个 union同时容纳字符串、整数、浮点、布尔、对象、数组外加一个 1 字节的类型标记。好处是任意深度都走同一套语法j[a][b][0]永远是合法表达式编译器不会在某一层要求你先转型文档也明说每个节点因此带有一个指针大小的联合体开销换来的是类型判断集中在内部用户不用碰。速度被明确放弃过。README 原话是当然存在更快的 JSON 库并给了第三方基准的引用。项目把性能换成了易用性和正确性测试上投入很重单元测试覆盖所有异常路径Google OSS-Fuzz 对各解析器 7×24 跑模糊测试仓库里 tests/reports/ 还留着 2016 年前后与 nativejson-benchmark 对比的历史数据。扩展走 ADL不走反射。库对未知类型零内置支持它只做一件事在序列化点用参数依赖查找去找你写的自由函数。没有宏、没有注册表、没有代码生成所以这套机制可以和任何命名空间布局组合。两个差异明显的实战场景配置加载让缺失键不致命问题配置里有些键是可选的逐个手写存在且类型正确的判断很啰嗦。做法value(key, default)一次解决存在性和默认值两件事。json j json::parse(std::ifstream(config.json)); int timeout j[server].value(timeout_ms, 3000); std::string log j.value(log_level, info); std::cout timeout log \n;效果单个键缺失只落到默认值程序继续跑不会出现一个可选字段没返回就把整个进程带崩的情况。内部交换同一份值两种格式问题HTTP 接口要对前端输出可读文本内部 RPC 却希望用紧凑二进制省流量。做法json值本身与格式无关输出时选接口即可。json req {{op, fetch}, {id, 42}}; // 面向浏览器文本 std::string http_body req.dump(); // 面向内部服务MessagePack 字节 std::vectorstd::uint8_t rpc_bytes json::to_msgpack(req); // 客户端侧原样还原 json back json::from_msgpack(rpc_bytes);效果两端代码写的都是同一套json操作格式差异被dump()与to_msgpack()吸收掉了BSON、CBOR、UBJSON、BJData 同理换格式只改调用处一行。深挖一层ADL 自定义类型转换这是库唯一一条扩展规则值得讲透。库本身不内置反射它对自定义类型的序列化完全依赖你提供的一对自由函数发现机制是 C 的 ADL编译器按函数参数所属的命名空间去查找找到即调用找不到就在getT()或j p处编译报错。struct Person { std::string name; int age 0; }; // 与 Person 同命名空间ADL 才能搜到这里 void to_json(json j, const Person p) { j json{{name, p.name}, {age, p.age}}; } void from_json(const json j, Person p) { p.name j.at(name).getstd::string(); p.age j.at(age).getint(); }写完之后json j p;和Person q j.getPerson();都能直接用。注意两个细节函数必须和类型放在同一命名空间否则 ADL 找不到这是新人最常见的坑at()在这里是故意的键缺失会抛out_of_range让反序列化失败显式化而不是悄悄给个默认值。枚举类型则可以直接套官方宏NLOHMANN_JSON_SERIALIZE_ENUM不用手写这一对函数。它快不快以及该和谁比性能上 README 自己承认这不是最快的 JSON 库仓库中留存的 nativejson-benchmark 数据里解析耗时与解析内存占用都落后于 RapidJSON 一档方向一致但量级差距不小作者的建议是按能否显著加快开发来选型而不是按峰值吞吐。正确性投入则很实所有解析器挂在 OSS-Fuzz 上持续跑。方案集成复杂度适用边界nlohmann/json拷一个头文件无构建系统依赖通用业务、配置、中小吞吐网络RapidJSON头文件可用DOM/SAX 两套 API模板较重解析吞吐敏感的服务器JsonCpp需编译静态库各包管理器均有传统构建体系、对 C 标准无要求官方文档与全部 API 示例在仓库内 docs/mkdocs/ 目录集成方式CMake、各包管理器见 README.md。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考