在 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
value 从 B
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.
的便利性
在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
value 从 B
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.
的便利性