nlohmann::json序列化嵌套结构体需手动定义同命名空间内的to_json/from_json自由函数,从最内层结构体开始逐层实现,参数须为const引用,嵌套调用自动触发,字段名与json key严格一致。

用 nlohmann::json 序列化嵌套结构体,核心不是“能不能”,而是“怎么让结构体可序列化”——它不自动反射,必须手动定义 to_json 和 from_json 两个非成员函数。
结构体必须显式声明序列化协议
nlohmann 不依赖宏或运行时反射,所有嵌套结构体都要逐层提供转换逻辑。没定义 to_json,编译直接报错:no matching function for call to 'to_json'。
- 每个自定义类型(包括内层结构体)都需在命名空间内声明两个自由函数:
to_json和from_json - 函数必须是
void返回,且第二个参数为 const 引用(const T&) - 不能放在类内部;若结构体在匿名命名空间里,
to_json也得在同一匿名命名空间中 - 嵌套越深,函数层级越要对齐:例如
A含B,B含C,那C的序列化必须先存在,否则B编译不过
嵌套结构体的 to_json 函数写法模板
以三层嵌套为例:Person → Address → GeoPoint,关键点是用 json::object() 构建键值对,字段名必须与 JSON key 一致,且类型要能被 nlohmann 自动处理(如 std::string、int、std::vector)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
struct GeoPoint {
double lat;
double lng;
};
struct Address {
std::string city;
GeoPoint location;
};
struct Person {
std::string name;
int age;
Address addr;
};
// 必须按嵌套顺序从内到外定义
void to_json(nlohmann::json& j, const GeoPoint& p) {
j = nlohmann::json{{"lat", p.lat}, {"lng", p.lng}};
}
void to_json(nlohmann::json& j, const Address& a) {
j = nlohmann::json{
{"city", a.city},
{"location", a.location} // 自动调用 GeoPoint 的 to_json
};
}
void to_json(nlohmann::json& j, const Person& p) {
j = nlohmann::json{
{"name", p.name},
{"age", p.age},
{"addr", p.addr} // 自动调用 Address 的 to_json
};
}
反序列化 from_json 容易漏掉 const 引用和异常处理
from_json 错误最常见的是忘记加 const,或没检查字段是否存在、类型是否匹配,导致运行时抛 nlohmann::json::type_error。
- 参数必须是
const nlohmann::json&和T&(非 const 引用,用于赋值) - 用
j.at("key")替代j["key"]:前者查不到直接 throw,后者返回 null 值,后续取.get<int>()</int>会 crash - 嵌套结构体字段也要用
.at(),比如j.at("addr").at("location").get<geopoint>()</geopoint> - 如果某些字段可选,改用
j.value("key", default_value),但注意它不校验类型
保存到文件时别忽略 UTF-8 和换行控制
nlohmann::json 默认输出紧凑格式(无空格、无换行),直接写入文件可读性差;但开 dump(2) 又可能因中文字段触发乱码——根本原因是 C++ 字符串字面量默认窄字符,而 nlohmann 内部全走 UTF-8。
- 确保源文件保存为 UTF-8 编码(VS Code / CLion 默认是,但 Windows 记事本不是)
- 结构体中
std::string存中文时,必须是 UTF-8 编码字节流(不是 GBK);否则dump()输出乱码 - 写文件推荐用
std::ofstream并设置.imbue(std::locale::classic())避免 locale 干扰 - 调试时用
std::cout 查看缩进格式,生产环境可选 <code>dump(-1)(最小化)
最常被跳过的一步:所有嵌套子结构体的 to_json/from_json 必须和结构体定义在同一个命名空间里——跨命名空间不生效,编译器找不到重载函数,错误提示极其模糊。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










