nlohmann/json序列化嵌套结构体必须手动定义同命名空间内的to_json/from_json自由函数,从最内层开始逐层实现,字段名与json key严格一致,否则编译报错。

用 nlohmann/json 直接映射嵌套对象到 C++ 类
能,但必须显式定义 from_json 和 to_json 两个友元函数,否则编译器无法自动推导嵌套结构的反序列化逻辑。nlohmann/json 不支持仅靠 public 成员变量就完成深层嵌套解析(比如 std::vector<inner></inner> 或 std::map<:string outer></:string> 这类组合)。
常见错误现象:error: no matching function for call to 'from_json' —— 多半是忘了给嵌套子类也加友元序列化函数,或字段名与 JSON key 不一致且没用 NLOHMANN_JSON_SERIALIZE_ENUM / JSONIFY 等辅助宏。
- 每个含嵌套成员的类都需独立定义
from_json/to_json,哪怕只是简单包装 - 字段名默认严格匹配 JSON key;若需别名,用
get<t>("key_name")</t>显式提取,或借助NLOHMANN_DEFINE_TYPE_INTRUSIVE宏配合json::value_t::object校验 - 容器类型如
std::optional<t></t>、std::variant需手动处理空值/多态分支,库不自动推断
处理缺失字段、类型不匹配和 null 值
JSON 中字段缺失或为 null 是常态,而 C++ 类成员默认不会被初始化为“未设置”状态。直接调用 json.at("field").get<int>()</int> 在字段不存在时抛 nlohmann::json::out_of_range;用 json.value("field", default_val) 又无法区分“缺省值”和“显式设为 default_val”。
推荐做法:对可选字段统一用 std::optional<t></t>,并在 from_json 中按需赋值:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
void from_json(const json& j, MyStruct& s) {
if (j.contains("items") && !j["items"].is_null()) {
s.items = j["items"].get<:vector>>();
}
// 注意:不赋值即保持 std::optional 的 nullopt 状态
}
</:vector>
-
j.is_null()必须在j.contains()后单独判断,因为j["missing"].is_null() == true(访问不存在 key 返回 null json) - 基础类型(
int,std::string)建议全用value()设默认值;复杂类型优先走contains()+is_null()分支 - 避免对
json::array调用get<:map>>()</:map>—— 类型不匹配会抛nlohmann::json::type_error
性能敏感场景下避免重复解析
如果同一个 JSON blob 要映射到多个不同类(比如先提 ID,再全量加载),反复调用 json::parse() 开销大,且容易因字段顺序/缩进差异导致哈希不一致。更稳妥的做法是解析一次,然后用引用传递子对象。
- 用
const json& root传参,内部用root["a"]["b"]["c"]链式访问,而非每次重新 parse 字符串 - 对高频访问字段,可提前缓存
json::const_iterator或json::pointer(如"/data/items/0/name"),但注意 pointer 在原 json 修改后失效 - 不要在循环里写
for (auto& item : j["list"]) { parse_item(item); }—— 若parse_item内部又调json::parse(...),就是典型重复解析
遇到递归结构或运行时类型怎么办
nlohmann/json 不支持自动处理递归定义(如 struct Node { std::vector<node> children; };</node>),也无法在解析时根据某个字段值(如 "type": "user")动态选择目标类。这类问题必须跳出自动映射,改用手动构造。
例如根据 type 字段分发:
if (j["type"] == "user") {
result = j.get<user>();
} else if (j["type"] == "admin") {
result = j.get<admin>();
}
</admin></user>
- 递归结构需改用指针(
std::unique_ptr<node></node>)并手写from_json,在构造子节点前检查深度防止栈溢出 - 运行时多态映射建议封装成工厂函数,把 type 判断和 get
拆开,方便单元测试 mock - 别试图用
std::any或std::variant统一接收——它们会让后续字段访问变得冗长且易错
最麻烦的不是语法,而是 JSON schema 和 C++ 类型契约不一致时,得靠人工补全校验逻辑。比如 JSON 里 "age" 是字符串,C++ 期望 int,这种隐式转换必须在 from_json 里显式写 std::stoi(j["age"].get<:string>())</:string>,而不是依赖库的自动转换。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










