关键在于避免崩溃:解析前检查 ParseResult 和 IsObject()/IsArray(),取值前用 HasMember() 和类型判断,遍历数组用 SizeType 并先校验 IsArray()。

用 RapidJSON 解析 JSON 数组和对象,关键不是“能不能”,而是“怎么避免崩溃”——ParseError、空指针访问、类型误判这三类问题占了 90% 的实际报错。
解析前必须检查 ParseResult 和 IsObject()/IsArray()
RapidJSON 不会自动抛异常,所有解析结果都靠手动校验。跳过这步,operator[] 或 GetArray() 极易触发段错误。
-
document.Parse(json_str.c_str())返回bool,必须判断是否为true - 即使解析成功,也要用
doc.IsObject()或doc.IsArray()明确顶层结构,不能默认是对象 - 对任意字段取值前,先用
HasMember("key")判断存在性,再用["key"].IsString()等确认类型
遍历 JSON 数组:用 SizeType 而非 int,且必须检查 IsArray()
数组长度类型是 rapidjson::SizeType(通常是 unsigned),直接用 int i = 0; i 在 32 位平台可能隐式截断;更危险的是,没检查 arr.IsArray() 就调 arr.Size(),会返回 0 并掩盖类型错误。
if (doc["items"].IsArray()) {
const rapidjson::Value& arr = doc["items"];
for (rapidjson::SizeType i = 0; i < arr.Size(); ++i) {
if (arr[i].IsObject() && arr[i].HasMember("id")) {
int id = arr[i]["id"].GetInt();
// ...
}
}
}访问嵌套对象字段:链式调用前每层都要判空
doc["data"]["user"]["name"].GetString() 这种写法看着简洁,但中间任一层缺失或类型不符,就会 crash。RapidJSON 不支持安全链式访问(类似 JS 的 optional chaining)。
立即学习“C++免费学习笔记(深入)”;
- 正确做法:逐层判断
HasMember()+IsXXX() - 或封装一个辅助函数,如
SafeGetString(const Value& v, const char* key),内部做完整校验 - 特别注意:字符串字段要用
GetString(),不是GetStringLength()—— 后者只返回长度,不保证以\0结尾
Document 生命周期必须长于所有引用它的 Value&
这是最容易被忽略的内存陷阱:把 document["items"] 存成局部 const Value& arr,然后在函数返回后还去遍历 arr —— document 已析构,arr 成悬垂引用,行为未定义。
- 所有
Value&引用必须确保其源Document对象仍在作用域内 - 若需跨函数传递数据,拷贝
Value(Value copy = doc["x"])或改用GenericValue配合自定义 allocator> - 不要试图用
std::move(document)来“转移所有权”——Document不可移动,移动后原对象处于无效状态
真正卡住人的从来不是语法,而是文档里没写的隐含约束:类型必须严判、引用必须守生命周期、数组索引必须用 SizeType。写完解析逻辑后,用一段含缺失字段、错类型、空数组的 JSON 多测几轮,比读十页 API 文档更管用。











