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

使用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)

解决方案

核心问题分析

  1. 顶层键不匹配:Cereal序列化单个对象时,会自动将其包装在value0键下,但反序列化时直接调用archive2(s2)会尝试读取顶层对象,而非value0内部的结构,导致RapidJSON断言失败。
  2. 枚举转换未处理失败场景: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 08:30:50