关键在于避免崩溃:解析前检查 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() 等确认类型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 (rapidjs
on::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)。
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> 配合自定义 allocatorstd::move(document) 来“转移所有权”——Document 不可移动,移动后原对象处于无效状态真正卡住人的从来不是语法,而是文档里没写的隐含约束:类型必须严判、引用必须守生命周期、数组索引必须用 SizeType。写完解析逻辑后,用一段含缺失字段、错类型、空数组的 JSON 多测几轮,比读十页 API 文档更管用。