如何使用 Cereal 序列化 json 对象而不将其包含在子对象中
How to serialize a json object without enclosing it in a sub-object using Cereal
假设我在 C++ 中有一个 class,如下所示:
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));
有什么方法可以利用我已经为 class 实现的序列化函数吗?
好的,明白了。很简单,只需要直接从对象调用序列化函数,传递存档,而不是将对象传递给存档。
Point p {1, 2, 3};
cereal::JSONOutputArchive ar(std::cout);
p.serialize(ar);
如果您预先知道要序列化的 class 有一个 serialize()
方法,那么本杰明的答案就是完美的解决方案。由于 Cereal 支持 in-class/out-of-class serialize()
、拆分 load()/save()
、显式版本控制;情况并非总是如此。 Cereal 的内部 cereal::InputArchive
和 cereal::OutputArchive
classes 都有一堆 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);
会自动selectPoint
提供的序列化方法,与Cereal内部做的一样。
您需要将您自己的类型的 epilogue
和 prologue
函数定义为无操作,这样就不会选择所选存档中的函数。
#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;
}
假设我在 C++ 中有一个 class,如下所示:
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));
有什么方法可以利用我已经为 class 实现的序列化函数吗?
好的,明白了。很简单,只需要直接从对象调用序列化函数,传递存档,而不是将对象传递给存档。
Point p {1, 2, 3};
cereal::JSONOutputArchive ar(std::cout);
p.serialize(ar);
如果您预先知道要序列化的 class 有一个 serialize()
方法,那么本杰明的答案就是完美的解决方案。由于 Cereal 支持 in-class/out-of-class serialize()
、拆分 load()/save()
、显式版本控制;情况并非总是如此。 Cereal 的内部 cereal::InputArchive
和 cereal::OutputArchive
classes 都有一堆 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);
会自动selectPoint
提供的序列化方法,与Cereal内部做的一样。
您需要将您自己的类型的 epilogue
和 prologue
函数定义为无操作,这样就不会选择所选存档中的函数。
#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;
}