nlohmann json C++ 库实战:一个头文件到底够不够用
【免费下载链接】jsonJSON for Modern C++项目地址: https://gitcode.com/GitHub_Trending/js/json
nlohmann json C++ 库(nlohmann/json)是一个单头文件的 C++ JSON 库,include 一个文件就能完成解析、修改、序列化,面向不想引入构建依赖的开发者,适合中小型项目和工具类代码。
🚀 nlohmann json 单头文件三步编译运行
从拿到代码到打印第一个结果,最短路径是四步:
- 克隆仓库:
git clone https://gitcode.com/GitHub_Trending/js/json - 写一个样例文件,include
single_include/下的头文件 - 编译:
g++ -std=c++11 -I json/single_include main.cpp -o demo - 运行
./demo
#include <nlohmann/json.hpp> #include <iostream> using json = nlohmann::json; int main() { json j = {{"name", "json"}, {"version", "3.12.0"}}; std::cout << j.dump(2) << std::endl; }这段代码构造了一个两字段对象,并用两格缩进打印出来,输出的就是熟悉的 JSON 结构。
为什么是单文件、零依赖
这个设计是拿编译时间换集成成本。单文件意味着放进任何工程都不用改构建系统,零依赖意味着不用追第三方包的版本。
这个单文件并不是手写的。源码实际放在include/nlohmann/目录,按输入、输出、迭代器等模块拆分,再由tools/amalgamate/里的脚本自动合并生成single_include/nlohmann/json.hpp。两条使用路径始终同步,你不需要关心哪份是"正本"。
代价是编译耗时:头文件约两万多行,包含它的每个编译单元都比普通库慢一些。多数项目可以接受;如果编译时间确实是瓶颈,C++20 用户可以用仓库自带的模块版本src/modules/json.cppm。
📁 nlohmann json 读写配置文件
先看最常见的场景:从文件读配置,缺省值兜底。
json config; std::ifstream f("config.json"); f >> config; std::string host = config.value("host", "localhost"); int port = config.value("port", 8080); for (auto& [key, value] : config.items()) { std::cout << key << " = " << value << "\n"; }这段代码把文件整体读成 json 对象,用value()安全取两个字段(键不存在时返回默认值),再遍历打印全部键值对。
对象内部的嵌套结构同样直接。j["a"]["b"]一路写下去就能构造深层嵌套,和写 JavaScript 对象几乎一样,不需要先声明类型再逐层填充。
🧩 nlohmann json 自定义类型序列化 to_json from_json
结构体和 JSON 的互转,靠两个自由函数完成:
struct Person { std::string name; int age; }; void to_json(json& j, const Person& p) { j = json{{"name", p.name}, {"age", p.age}}; } void from_json(const json& j, Person& p) { j.at("name").get_to(p.name); j.at("age").get_to(p.age); } Person p{"Ada", 36}; json j = p; // 自动序列化 Person q = j.get<Person>(); // 自动反序列化库通过 ADL 机制在赋值和get<T>()时找到你定义的to_json/from_json,业务代码里就只剩两次转换。
📍 用 JSON Pointer 和 JSON Patch 做增量修改
定位深层字段不必层层取值,RFC 6901 风格的指针可以直接寻址;修改整棵树的增量操作则交给 JSON Patch:
json j = R"({"server": {"port": 8080, "hosts": ["a", "b"]}})"_json; std::cout << j["/server/hosts/1"_json_pointer] << std::endl; // b json source = {{"baz", "qux"}, {"foo", "bar"}}; json patch = R"([{"op": "replace", "path": "/baz", "value": "boo"}, {"op": "remove", "path": "/foo"}])"_json; std::cout << source.patch(patch) << std::endl;前四行用指针/server/hosts/1取出数组第二个元素;后四行把一段 patch 描述应用到 source 上,得到{"baz":"boo"}。远程配置下发、协作编辑这类"传操作不传全量"的场景可以直接套用。
⚖️ 能力边界:哪些场景该换库
它擅长的是一般业务场景:配置读取、接口数据交换,以及 CBOR、MessagePack、BSON 等二进制格式之间的互转,Pointer 和 Patch 也内置。
不适合的是追求极限解析速度的热路径,以及内存受限环境。从仓库自带的官方 benchmark 看,它的解析耗时在一二十个库里处于中游:
nlohmann json C++ 库解析耗时 benchmark 对比图.png>)
日常够用,但不是最快。性能敏感时可以考虑替代方案:
| 关注点 | nlohmann/json | RapidJSON | simdjson |
|---|---|---|---|
| 集成成本 | 拷贝一个头文件 | 需构建 | 需构建 |
| API 风格 | 运算符重载,写法像 Python | DOM/SAX 双模式,偏底层 | 解析器优先,单线程最快 |
| 适合场景 | 通用业务,中小 JSON | 性能与内存敏感 | 大批量数据解析 |
🔌 工程集成:CMake 拉取与包管理器
CMake 工程推荐用 FetchContent 把仓库拉成依赖:
include(FetchContent) FetchContent_Declare(nlohmann_json GIT_REPOSITORY https://gitcode.com/GitHub_Trending/js/json GIT_TAG master) FetchContent_MakeAvailable(nlohmann_json) target_link_libraries(app PRIVATE nlohmann_json::nlohmann_json)这段配置把库变成一个普通的 CMake 目标,后续升级只改GIT_TAG。
没有构建系统的工程,直接把single_include/nlohmann/json.hpp拷进自己的 include 目录即可。包管理器渠道也齐全:vcpkg 执行vcpkg install nlohmann-json,Homebrew 执行brew install nlohmann-json,Ubuntu 执行apt install nlohmann-json3-dev,Conan 可搜到nlohmann_json配方。版本变化记录在 ChangeLog.md,API 细节查 官方文档。
⚠️ 常见坑与实用技巧
const json 上 operator[] 会抛异常。只读场景不能用j["key"],它试图构造键。改用j.value("key", 默认值)或j.at("key"),前者缺键时安静返回默认值,后者抛out_of_range便于定位问题。
dump 默认转义中文。非 ASCII 字符会变成\uXXXX序列。要让中文原样输出,用j.dump(2, false),第二个参数就是ensure_ascii。
对象同键赋值会覆盖。对同一键二次赋值,旧值直接丢失。如果语义是"不存在才插入",用j.emplace("key", value)。
可以先拿单头文件跑起来验证配置处理需求;如果业务涉及高频解析大数据,建议先做一轮基准测试再定。对大多数 C++ 项目来说,它是那个不用想太多就能上手的 JSON 库。
【免费下载链接】jsonJSON for Modern C++项目地址: https://gitcode.com/GitHub_Trending/js/json
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考