使用cereal库反序列化含枚举的JSON触发RapidJSON断言错误
问题:Cereal反序列化含枚举的JSON失败(magic_enum做字符串转换)
我尝试用Cereal序列化库输出枚举的字符串表示,借助magic_enum库实现枚举与字符串的转换。序列化MyStruct到JSON的过程正常,但反序列化时抛出错误。相关代码、生成的JSON及错误信息如下:
代码
#include <fmt/core.h> #include <magic_enum.hpp> #include <cereal/archives/json.hpp> #include <fstream> enum class EnumType : int { A, B, C }; template<class Archive, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae, class T> std::enable_if_t<std::is_enum_v<T>, std::string> save_minimal(Archive &, const T &h) { return std::string(magic_enum::enum_name(h)); } template<class Archive, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae, class T> std::enable_if_t<std::is_enum_v<T>, void> load_minimal(Archive const &, T &enumType, std::string const &str) { enumType = magic_enum::enum_cast<T>(str).value(); } struct MyStruct { EnumType e; std::string s; // serialize template<class Archive> void serialize(Archive &archive) { archive(CEREAL_NVP(e), CEREAL_NVP(s)); } }; int main() { std::string json_file = "s.json"; // serialize to JSON MyStruct s{EnumType::A, "xmcnbx"}; std::ofstream os(json_file); cereal::JSONOutputArchive archive(os); // ok archive(s); // deserialize from JSON MyStruct s2; std::ifstream is(json_file); cereal::JSONInputArchive archive2(is); // error! archive2(s2); fmt::print("s2.e = {}, s2.s = {}", magic_enum::enum_name(s2.e), s2.s); }
生成的s.json
{ "value0": { "e": "A", "s": "xmcnbx" } }
错误信息
libc++abi: terminating with uncaught exception of type cereal::RapidJSONException: rapidjson internal assertion failure: IsObject() Process finished with exit code 134 (interrupted by signal 6: SIGABRT)
解决方案
核心问题分析
- 顶层键不匹配:Cereal序列化单个对象时,会自动将其包装在
value0键下,但反序列化时直接调用archive2(s2)会尝试读取顶层对象,而非value0内部的结构,导致RapidJSON断言失败。 - 枚举转换未处理失败场景:
magic_enum::enum_cast可能返回空optional,直接调用value()会触发未捕获的异常。
修复步骤
1. 匹配顶层键完成反序列化
修改main函数中的反序列化代码,显式指定读取value0键:
// 反序列化从JSON MyStruct s2; std::ifstream is(json_file); cereal::JSONInputArchive archive2(is); archive2(cereal::make_nvp("value0", s2)); // 匹配序列化时自动生成的value0键
2. 完善枚举转换的异常处理
修改load_minimal函数,处理转换失败的情况:
template<class Archive, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae, class T> std::enable_if_t<std::is_enum_v<T>, void> load_minimal(Archive const &, T &enumType, std::string const &str) { auto enum_opt = magic_enum::enum_cast<T>(str); if (!enum_opt) { throw std::invalid_argument(fmt::format("无法将字符串\"{}\"转换为目标枚举类型", str)); } enumType = *enum_opt; }
3. 可选:自定义顶层键(更友好的JSON结构)
如果不想使用默认的value0,可以在序列化时指定自定义顶层键,让JSON结构更清晰:
- 序列化部分修改:
cereal::JSONOutputArchive archive(os); archive(cereal::make_nvp("MyStruct", s));
- 反序列化对应修改:
archive2(cereal::make_nvp("MyStruct", s2));
此时生成的JSON会变为:
{ "MyStruct": { "e": "A", "s": "xmcnbx" } }
内容的提问来源于stack exchange,提问作者marital_weeping
相关产品推荐
相关产品推荐

