You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

导出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节点(包括对象键、字符串值等)。

  1. 原始代码的问题:

    • 创建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()断言失败。
  2. 修改后的解决原理:

    • 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:42:46