如何序列化json对象而不使用Cereal将其封闭在子对象中

时间:2015-11-15 22:54:30

标签: c++ json serialization cereal

让我们说我有一个C ++课程,如下所示:

struct Point {
    int x, y, z;
};

我想使用Cereal将该结构序列化为JSON。所以我添加了一个这样的序列化函数:

struct Point {
    int x, y, z;
    template<class Archive>
    void serialize(Archive& ar) {
        ar(CEREAL_NVP(x),
           CEREAL_NVP(y),
           CEREAL_NVP(z));
    }
};

当Point是另一个对象的成员或数组的元素时,这可以正常工作。但是如果我想让Point成为整个JSON文件的主要对象,它就无法正常工作。例如,使用以下代码:

Point p { 1, 2, 3 };
cereal::JSONOutputArchive ar(std::cout);
ar(p);

我得到以下输出:

{
    "value0": {
        "x": 1,
        "y": 2,
        "z": 3
    }
}

我想删除"value0"密钥并提升对象以占用整个文件,如下所示:

{
    "x": 1,
    "y": 2,
    "z": 3
}

我能做的唯一方法就是基本上重新实现序列化功能,手动添加密钥名称。

Point p {1, 2, 3};
cereal::JSONOutputArchive ar(std::cout);
ar(cereal::make_nvp("x", p.x),
   cereal::make_nvp("y", p.y),
   cereal::make_nvp("z", p.z));

有没有办法利用我已经为类实现的序列化函数来实现它?

3 个答案:

答案 0 :(得分:5)

好的,想通了。非常简单,只需要直接从对象调用serialize函数,传递存档,而不是将对象传递给存档。

Point p {1, 2, 3};
cereal::JSONOutputArchive ar(std::cout);
p.serialize(ar);

答案 1 :(得分:1)

如果您事先知道要序列化的类具有serialize()方法,那么Benjamin的答案是完美的解决方案。由于Cereal支持课堂内/课外serialize(),因此可以分割load()/save(),显式版本;并非总是如此。谷歌的内部cereal::InputArchivecereal::OutputArchive类都有一堆SFINAE模板方法来检测在编译期间使用的正确序列化方法。那里的类​​型特征可用于滚动我们自己的模板开关:

template< typename Class, typename Archive,
          typename std::enable_if< cereal::traits::has_member_serialize<Class, Archive>::value>::type* = nullptr>
inline static void serializeHelper(Class& cl, Archive& ar)
{
    cl.serialize(ar);
}

template< typename Class, typename Archive,
          typename std::enable_if< cereal::traits::has_member_save<Class, Archive>::value>::type* = nullptr>
inline static void serializeHelper(Class& cl, Archive& ar)
{
    cl.save(ar);
}

// More version could follow for remaining serialization types (external, versioned...)

template< typename Class, typename Archive,
          typename std::enable_if< cereal::traits::has_member_serialize<Class, Archive>::value>::type* = nullptr>
inline static void deserializeHelper(Class& cl, Archive& ar)
{
    cl.serialize(ar);
}

template< typename Class, typename Archive,
          typename std::enable_if< cereal::traits::has_member_load<Class, Archive>::value>::type* = nullptr>
inline static void deserializeHelper(Class& cl, Archive& ar)
{
    cl.load(ar);
}

// More version could follow for remaining deserialization types (external, versioned...)

调用serializeHelper(p, ar);会自动选择Point提供的序列化方法,就像谷歌内部一样。

答案 2 :(得分:1)

您需要为自己的类型将epilogueprologue函数定义为no-ops,以便不会选择来自所选存档的函数。

#include <cereal/archives/json.hpp>
#include <iostream>

struct Point {
    int x, y, z;
    template<class Archive>
    void serialize(Archive& ar) {
        ar(CEREAL_NVP(x),
           CEREAL_NVP(y),
           CEREAL_NVP(z));
    }
};

void epilogue(cereal::JSONOutputArchive&, const Point &){}
void prologue(cereal::JSONOutputArchive&, const Point &){}

int main()
{
    Point p { 1, 2, 3 };
    cereal::JSONOutputArchive ar(std::cout);
    ar(p); 

    return 0;
}