导出rapidjson::Document为字符串时触发崩溃问题求助
RapidJSON 断言失败
m->name.IsString() 问题排查与原因解析 问题重现
以下代码在调用buffer.GetString()时触发致命信号6(SIGABRT),中止信息显示断言m->name.IsString()失败:
rapidjson::Document doc; doc.SetObject(); // insert some sub JsonObjects rapidjson::Document subA; if (!subA.Parse<0>(jsonStringA->c_str()).HasParseError()) { doc.AddMember("A", subA, doc.GetAllocator()); } rapidjson::Document subB; if (!subB.Parse<0>(jsonStringB->c_str()).HasParseError()) { doc.AddMember("B", subB, doc.GetAllocator()); } // Export document as string rapidjson::StringBuffer buf; rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buf); doc.Accept(writer); buffer.GetString();
错误断言信息:
/document.h:1778: bool rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> >::Accept(Handler &) const [Encoding = rapidjson::UTF8<char>, Allocator = rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, Handler = rapidjson::PrettyWriter<rapidjson::GenericStringBuffer<rapidjson::UTF8<char>, rapidjson::CrtAllocator>, rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator, 0>]: assertion "m->name.IsString()" failed'
将子Document的初始化改为rapidjson::Document subA(doc.GetAllocator())后问题解决,具体原因解析如下:
原因解析
RapidJSON的核心设计中,每个Document默认会创建独立的内存池(MemoryPoolAllocator),用来存储解析生成的JSON节点(包括对象键、字符串值等)。
原始代码的问题:
- 创建
rapidjson::Document subA;时,subA会初始化自己的私有内存池,解析jsonStringA后,所有子节点(包括对象的键)都存在这个独立内存池中。 - 调用
doc.AddMember("A", subA, doc.GetAllocator())时,仅将subA的根Value结构复制到doc中,但subA内部节点的内存仍属于subA的私有内存池。 - 当
doc.Accept(writer)遍历JSON结构时,访问subA对应的子对象成员的键时,RapidJSON的断言会检查该键的有效性。由于这些键的内存由subA的分配器管理,与doc的分配器不共享,遍历逻辑无法正确识别其为有效字符串,触发m->name.IsString()断言失败。
- 创建
修改后的解决原理:
- 用
rapidjson::Document subA(doc.GetAllocator())初始化子Document时,subA会直接复用doc的内存池分配器。 - 解析
jsonStringA生成的所有节点都会存储在doc的内存池中,调用AddMember时,subA的Value内容可以被doc安全接管,遍历整个JSON结构时所有节点的内存都由同一个分配器管理,断言检查自然通过。
- 用
替代方案
除了让子Document复用父Document的分配器,还可以用Value替代子Document,或者显式调用CopyFrom方法将子节点复制到父Document的内存池:
// 方案1:用Value替代子Document rapidjson::Value subA(rapidjson::kObjectType); subA.Parse<0>(jsonStringA->c_str()); if (!subA.HasParseError()) { doc.AddMember("A", subA, doc.GetAllocator()); } // 方案2:使用CopyFrom rapidjson::Document subA; if (!subA.Parse<0>(jsonStringA->c_str()).HasParseError()) { rapidjson::Value subValue; subValue.CopyFrom(subA, doc.GetAllocator()); doc.AddMember("A", subValue, doc.GetAllocator()); }
内容的提问来源于stack exchange,提问作者indianpapa
相关产品推荐
相关产品推荐

