应检查空指针、非null-terminated字符串(改用c_str())、输入完整性、类型判断(IsObject())、字段存在性(HasMember)、避免Value拷贝、复用Document需SetNull()、线程不安全、UTF-8编码验证及生命周期管理。

rapidjson::Document::Parse() 返回 kParseErrorInvalidValue 怎么办
多数时候不是 JSON 格式错,而是传入了空指针或非 null-terminated 字符串。RapidJSON 默认只接受以 \0 结尾的 C 风格字符串,如果用 std::string::data() 直接传入(尤其在空字符串或 move 后),可能没结尾符。
- 安全做法:用
std::string::c_str()替代data(),确保 null 终止 - 若 JSON 来自网络或文件流,先确认读取完整且未截断(比如
recv()返回值未校验、fread()没检查实际字节数) - 调试时可先用
printf("%s", json_str.c_str())看是否能正常打印——不能,说明字符串本身已损坏
解析嵌套对象时访问 memberEnd() 越界崩溃
RapidJSON 的 Value::MemberIterator 不是随机访问迭代器,it + n 会 UB;而且 MemberBegin()/MemberEnd() 只在 IsObject() 为 true 时有效,否则行为未定义。
- 必须先判类型:
if (obj.IsObject()) { for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { ... } } - 访问字段前务必检查是否存在:
if (obj.HasMember("user")) { const auto& u = obj["user"]; },直接写obj["user"]在 key 不存在时会触发内部断言(Release 模式下静默返回空值,但后续调用GetString()等会 crash) - 不要对
Value做拷贝传递——它不管理内存,拷贝后原对象析构会导致悬垂引用
如何避免重复解析同一份 JSON 字符串
rapidjson::Document 解析后持有全部数据的内存,但默认不支持 reuse —— 每次调用 Parse() 前需显式调用 SetNull() 或 Parse() 重载版本指定 allocator。
- 最简复用方式:
doc.SetNull(); doc.Parse(json_str.c_str()); - 若频繁解析不同 JSON,建议提前分配足够大的
rapidjson::MemoryPoolAllocator并传给 Document 构造函数,减少堆分配开销 - 注意:
Document不是线程安全的,多线程必须每个线程独享实例,或加锁
从 Value 提取字符串时出现乱码或崩溃
RapidJSON 默认使用 UTF-8 编码,但不自动做编码转换;GetString() 返回的是原始字节指针,若源 JSON 含 BOM 或用了 UTF-16/UTF-32,解析会失败或返回不可靠指针。
立即学习“C++免费学习笔记(深入)”;
- 确认输入 JSON 确实是合法 UTF-8:可用
rapidjson::UTF8验证器预检,或用工具如iconv -f utf-8 -t utf-8//strict过滤 -
GetString()返回的指针生命周期绑定于Document,Document 析构后立即失效——别存裸指针,要用就std::string(str, len)拷贝 - 中文字段名或值没问题,但若 JSON 中混用了全角引号、不可见控制字符(如 \u200b),
Parse()会静默失败,建议解析前用正则清理空白字符
最常被忽略的是 Document 生命周期和字符串所有权关系——它不复制 JSON 内容,只是解析索引,一旦 Document 销毁,所有 GetString()、GetArray() 返回的视图全部失效。


















