如何解决C++ cereal库的JSON序列化与反序列化错误?
Cereal库JSON序列化/反序列化报错:rapidjson internal assertion failure: IsObject()
问题现象
使用cereal库进行JSON序列化时触发运行时错误:libc++abi: terminating with uncaught exception of type cereal::RapidJSONException: rapidjson internal assertion failure: IsObject()
二进制归档模式完全正常,但JSON序列化生成的文件顶层节点未闭合,例如生成的c.txt内容为:
{ "value0": 321
缺少结尾的},导致反序列化失败。
通用序列化/反序列化模板代码
enum SER_Archive_Type {BIN, JSON}; template<typename T> bool SerializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) { std::fstream fs; switch (TYPE) { case JSON: { fs.open(filename, std::ios::out); if (fs.is_open()) { cereal::JSONOutputArchive jarchive(fs); jarchive(obj); fs.close(); return true; } else { return false; } break; } case BIN: { fs.open(filename, std::ios::out | std::ios::binary); if (fs.is_open()) { cereal::BinaryOutputArchive barchive(fs); barchive(obj); fs.close(); return true; } else { return false; } break; } default: break; } return false; } template<typename T> bool DeserializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) { std::fstream fs; switch (TYPE) { case JSON: { fs.open(filename, std::ios::in); if (fs.is_open()) { cereal::JSONInputArchive jarchive(fs); jarchive(obj); fs.close(); return true; } else { return false; } break; } case BIN: { fs.open(filename, std::ios::in | std::ios::binary); if (fs.is_open()) { cereal::BinaryInputArchive barchive(fs); barchive(obj); fs.close(); return true; } else { return false; } break; } default: break; } return false; }
测试代码
int main() { uint32_t a = 123; SerializeObject(a, "a.bin", BIN); // ok uint32_t b; DeserializeObject(b, "a.bin", BIN); // ok cout << b << endl; uint32_t c = 321; SerializeObject(c, "c.txt", JSON); // ok uint32_t d; DeserializeObject(d, "c.txt", JSON); // error cout << b << endl; }
问题原因
手动提前关闭文件流是核心问题。cereal的JSONOutputArchive需要在析构时完成JSON的收尾工作(比如添加闭合的}),但当前代码在调用jarchive(obj)后立刻执行fs.close(),此时JSONOutputArchive尚未析构,导致文件写入不完整,顶层JSON对象未闭合。二进制归档不受影响是因为其写入逻辑无需延迟到析构时完成收尾操作。
解决方法
确保JSONOutputArchive先析构,再关闭文件流,有两种实现方式:
方式1:用代码块限制归档对象的作用域
template<typename T> bool SerializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) { std::fstream fs; switch (TYPE) { case JSON: { fs.open(filename, std::ios::out); if (fs.is_open()) { { // 新增代码块,归档对象在此块结束时自动析构 cereal::JSONOutputArchive jarchive(fs); jarchive(obj); } // 析构完成,JSON收尾写入完成 fs.close(); return true; } else { return false; } break; } case BIN: { fs.open(filename, std::ios::out | std::ios::binary); if (fs.is_open()) { cereal::BinaryOutputArchive barchive(fs); barchive(obj); fs.close(); return true; } else { return false; } break; } default: break; } return false; }
方式2:移除手动fs.close(),依赖文件流自动析构
template<typename T> bool SerializeObject(T &obj, std::string filename, const SER_Archive_Type TYPE) { switch (TYPE) { case JSON: { std::fstream fs(filename, std::ios::out); if (fs.is_open()) { cereal::JSONOutputArchive jarchive(fs); jarchive(obj); return true; } else { return false; } break; } case BIN: { std::fstream fs(filename, std::ios::out | std::ios::binary); if (fs.is_open()) { cereal::BinaryOutputArchive barchive(fs); barchive(obj); return true; } else { return false; } break; } default: break; } return false; }
同理,反序列化函数中也可以移除手动的fs.close(),避免潜在问题。
内容的提问来源于stack exchange,提问作者drmax
相关产品推荐
相关产品推荐

