在提升序列化中平展嵌套的命名值对

Flatten nested named value pair in Boost Serialization

本文关键字:嵌套 升序 序列化      更新时间:2023-10-16

在Boost.Serialization中序列化的规范方式是定义一个serialize(或load/save(函数,该函数将对象的值状态转换为一系列更原始的值。如果序列化归档文件需要命名值对,则还需要定义名称。

例如

struct A{
double x; int y; unsigned z;
template<class Ar>
void serialize(Ar& ar, unsigned){
ar 
& BOOST_SERIALIZATION_NVP(x) 
& BOOST_SERIALIZATION_NVP(y) 
& BOOST_SERIALIZATION_NVP(z)
;
}
}
...
A a;
boost::archive::xml_oarchive xoa{ofs};
xoa << BOOST_SERIALIZATION(a);

生成类似于以下内容的结构:

<a>
<x>1.2</x>
<y>2</y>
<z>3</z>
</a>

但是,此语法会引入层次结构,有时不需要该层次结构,例如在使用(普通(继承时,扩展类。

struct B{
double x; int y;
template<class Ar>
void serialize(Ar& ar, unsigned){
ar 
& BOOST_SERIALIZATION_NVP(x) 
& BOOST_SERIALIZATION_NVP(y) 
;
}
};
struct C : B{
unsigned z;
template<class Ar>
void serialize(Ar& ar, unsigned){
B& B_part = (*this);
ar 
& BOOST_SERIALIZATION_NVP(B_part) // uses code from base
& BOOST_SERIALIZATION_NVP(z) 
;
}
};
...
C c; c.x = 1.2; c.y = 2; c.z = 3;
boost::archive::xml_oarchive xoa{ofs};
xoa << BOOST_SERIALIZATION_NVP(c);
<c>
<B_part>
<x>1.2</x>
<y>2</y>
</B_part>
<z>3</z>
</a>

这有点人为。有没有办法强制档案馆扁平化这个参考?并得到这个:

<c>
<x>1.2</x>
<y>2</y>
<z>3</z>
</a>

当然,我可以将结构重写为

struct C : B{
unsigned z;
template<class Ar>
void serialize(Ar& ar, unsigned){
ar 
& BOOST_SERIALIZATION_NVP(x)
& BOOST_SERIALIZATION_NVP(y)
& BOOST_SERIALIZATION_NVP(z) 
;
}
};

但是这变得很麻烦,因为我必须在派生类中重复基类中的所有代码。

例如,假设的代码可以是这样的:

void serialize(Ar& ar, unsigned){
B& B_part = (*this);
ar 
& BOOST_SERIALIZATION_UNNAMED(B_part) // uses code (and names) from base, does not create a new level
& BOOST_SERIALIZATION_NVP(z) 
;
}

这种基类的序列化,而不生成较低的级别,是否在库中考虑过什么?

我知道 Boost.Serialization 仍然执行序列化工作,但是 XML 可以通过这种方式变得更加可读。

Boost.Serialization 有很多关于序列化基类和派生类(用于经典多态继承((https://www.boost.org/doc/libs/1_70_0/libs/serialization/doc/serialization.html#base(的文档,但它似乎没有关于使用继承类进行聚合的文档。

抵制将

*this 投射到基类的诱惑。这似乎有效,但可能无法调用正确序列化所需的代码。

你是对的,如果基类不是多态的,则不是绝对必要的。这是一个建议。但我坚持这一建议。 最好有一个简单的规则,而不是必须重新考虑每个实例。

我质疑尝试将xml文件"塑造"为某些特定口味和/或外观要求的价值。 这会将C++数据结构耦合以履行其他角色。 如果你真的需要使xml文件"可读"或其他什么,一个更好的方法是使用其他工具(如xslt(来修改/过滤序列化库生成的xml。 这将有效地允许您为程序制作最佳的C++数据结构,同时保留处理 xml 的便利性。

我找到了一种方法,(我不知道它是否只是偶然起作用(。

struct C : B{
unsigned z;
template<class Ar>
void serialize(Ar& ar, unsigned u){
B::serialize(ar, u); // base class serialization
// or boost::serialization::serialize(ar, static_cast<B&>(*this), u);
ar 
& BOOST_SERIALIZATION_NVP(y)
& BOOST_SERIALIZATION_NVP(z) 
;
}
};

(我之前拒绝了这个解决方案,因为我认为它会生成一个重复的 XML 标头,因为它似乎将一个存档嵌套在另一个存档中,但事实并非如此。

这会将任何其他结构平展到归档中,而不仅仅是基类。此外,boost::serialization::serialize在设计上不适用于基元类型,因此您永远不会得到"未命名"的叶节点。

这将生成所需的结构并重用基库中的代码:

<c>
<x>1.2</x>
<y>2</y>
<z>3</z>
</c>

注意:这正是手册在序列化派生类时不要做的事情,https://www.boost.org/doc/libs/1_70_0/libs/serialization/doc/serialization.html#base

抵制将*this投射到基类的诱惑。这似乎有效,但可能无法调用正确序列化所需的代码。

但我认为他指的是(反(多态序列化,从派生实例中保存并在基(指针(中加载实例。

无论如何,这种用法在这里永远不会起作用,因为从B序列化的东西反序列化为C,或者相反,arround 将是值语义哲学中的逻辑错误(以及运行时错误,因为它们具有不同数量的成员(。