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

Flatten nested named value pair in Boost Serialization

在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>

然而,这种语法会引入层次结构,有时并不需要这种层次结构,例如在使用(普通)继承时扩展 class。

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>

这有点做作。有没有办法强制存档将此 refepresentation 展平? 并改为获取此:

<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) 
     ;
   }
};

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

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

   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) 
     ;
   }

这种基础class的序列化,不生成低层,库中考虑的东西吗?

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

Boost.Serialization 有很多关于序列化基础和派生 classes 的文档(对于 classic 多态继承)(https://www.boost.org/doc/libs/1_70_0/libs/serialization/doc/serialization.html#base),但似乎没有获得关于这种使用继承的文档 classes for aggregation。

我找到了一个方法,(我不知道它是否只是偶然)。

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 header,因为它似乎将一个存档嵌套在另一个存档中,但事实并非如此。)

这会将任何其他结构展平到存档中,而不仅仅是基础 class。此外,boost::serialization::serialize 在设计上不适用于基本类型,因此您永远不会以 "unnamed" 叶节点结束。

这会生成所需的结构并重新使用基础代码:

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

注意:这正是手册所说的在序列化派生的class、[=17时不要做的事情=]

Resist the temptation to just cast *this to the base class. This might seem to work but may fail to invoke code necessary for proper serialization.

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

这种用法在这里无论如何都行不通,因为反序列化为 C valueB value[=38 序列化的东西=],或者反过来将是 value-semantics 哲学中的逻辑错误(以及运行时错误,因为它们具有不同数量的成员)。

Resist the temptation to just cast *this to the base class. This might seem to work but may fail to invoke code necessary for proper serialization.

你说得对,如果基础 class 不是多态的,那么它并不是绝对必要的。这是一个建议。但我支持这个建议。最好有一个简单的规则,而不是必须重新考虑每个实例。

我质疑尝试 "shape" xml 文件以满足某些特定口味 and/or 外部要求的价值。这结合了 C++ 数据结构来完成一些其他的角色。如果您真的需要制作 xml 文件 "readable" 或其他任何东西,更好的方法是使用一些其他工具,例如 xslt 来 modify/filter 生成的 xml序列化库。这将有效地允许您为程序创建最佳的 C++ 数据结构,同时保留处理 xml.

的便利性