用nlohmann::json读取嵌套对象和数组应优先使用at()安全访问并配合is_object()/is_array()类型检查,缺失字段用contains()判断或封装get_or()函数处理,默认值需注意类型匹配,性能敏感时用get_ref避免拷贝。

如何用 nlohmann::json 读取嵌套对象和数组
nlohmann 库把 JSON 当作树形结构处理,嵌套对象就是 json 类型的值,不是字符串或原始指针。直接用 operator[] 可以链式访问,但必须确保每层键都存在,否则会静默创建空对象(导致逻辑错误)。
推荐用 at() 替代 operator[] 做安全访问——它在键不存在时抛出 nlohmann::json::out_of_range 异常,便于定位问题:
json j = R"({"user":{"profile":{"name":"Alice","tags":["dev","cpp"]}}})"_json;
try {
std::string name = j.at("user").at("profile").at("name").get<std::string>();
auto tags = j.at("user").at("profile").at("tags").get<std::vector<std::string>>();
} catch (const json::out_of_range& e) {
// 处理缺失字段
}
-
at()是只读、安全、带异常检查的访问方式;operator[]是可写、自动补缺、无提示的访问方式 - 嵌套过深时,建议拆成中间变量,避免长链调用掩盖哪一层出错
- 对不确定是否存在的字段,先用
contains()判断再访问,比 try/catch 更轻量
解析动态结构:如何判断某个嵌套节点是 object 还是 array
JSON 数据可能在运行时变化结构(比如 API 返回中 data 有时是对象、有时是数组),不能硬写 .at("data").get<json>() 后直接当对象用。
必须用 is_object()、is_array()、is_string() 等类型检查函数预先判断:
立即学习“C++免费学习笔记(深入)”;
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
json data_node = j.at("data");
if (data_node.is_object()) {
// 解析为单个对象
int id = data_node.at("id").get<int>();
} else if (data_node.is_array()) {
// 解析为对象数组
for (const auto& item : data_node) {
if (item.is_object() && item.contains("id")) {
std::cout << item.at("id").get<int>() << "\n";
}
}
}
- 不要依赖
dump()输出字符串去匹配关键字判断类型,效率低且易错 -
is_null()必须显式检查,尤其在可选字段为空时,否则at()会抛异常 - 类型检查后,再用
get<T>()转换,避免bad_cast
处理缺失字段与默认值的惯用写法
nlohmann 不提供类似 JavaScript 的 ?. 或 Python 的 dict.get("key", default) 语法,但可以用逻辑短路 + 三元表达式模拟:
std::string avatar = j.contains("user") && j["user"].contains("avatar")
? j["user"]["avatar"].get<std::string>()
: "default.png";
更清晰的做法是封装一个工具函数:
template<typename T>
T get_or(const json& j, const std::string& key, const T& def) {
return j.contains(key) ? j.at(key).get<T>() : def;
}
// 使用
int timeout = get_or(j, "timeout", 30);
- 避免用
operator[]访问后直接get<T>(),因为["missing"]会新建空json,再get<int>()抛type_error - 对数字类型,默认值要小心隐式转换,比如传
0给get<double>()没问题,但传"0"就会失败 - 如果整个路径都可能缺失(如
"config.db.host"),建议写递归查找或用第三方扩展如json-pointer
性能敏感场景:避免重复解析和临时拷贝
每次调用 at() 或 operator[] 都返回一个新的 json 引用(内部是 const 引用),开销小;但频繁调用 get<std::string>() 会触发字符串拷贝,尤其在循环中解析大量字段时明显。
关键优化点:
- 用
get_ref<const std::string&>()获取只读引用,避免拷贝(注意:引用生命周期绑定到原json对象) - 批量解析时,优先用结构化绑定(C++17)或自定义
from_json,而不是手动逐字段get - 不要把大 JSON 文本反复
parse()—— 一次解析,多次访问;若需多线程读,json对象本身是线程安全的(只读)
嵌套深、字段多、数据量大时,最易被忽略的是「默认构造空 json 对象」行为——它不报错,但后续逻辑全错,调试时得逐层加 contains() 或日志输出 dump(2) 查结构。

















