如何在 boost 序列化中更改或删除标签?
Posted
技术标签:
【中文标题】如何在 boost 序列化中更改或删除标签?【英文标题】:How to change or delete tags in boost serialization? 【发布时间】:2017-11-23 12:00:23 【问题描述】:我正在尝试将我的类序列化为 xml。我的课;
class HardwareDto
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & ar, const unsigned int version)
ar & BOOST_SERIALIZATION_NVP(HardwareID);
ar & BOOST_SERIALIZATION_NVP(HardwareHostID);
ar & BOOST_SERIALIZATION_NVP(HardwareFriendlyName);
public:
int HardwareID;
int HardwareHostID;
string HardwareFriendlyName;
inline HardwareDto(int HardwareHostID, int HardwareID, string HardwareFriendlyName)
this->HardwareHostID = HardwareHostID;
this->HardwareID = HardwareID;
this->HardwareFriendlyName = HardwareFriendlyName;
;
还有一个包含HardwareDto
列表的类。
class HardwareHostDto
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & ar, const unsigned int version)
ar & BOOST_SERIALIZATION_NVP(HardwareHostID);
ar & BOOST_SERIALIZATION_NVP(BranchID);
ar & BOOST_SERIALIZATION_NVP(HardwareHostFriendlyName);
ar & BOOST_SERIALIZATION_NVP(HardwareList);
public:
int HardwareHostID;
int BranchID;
string HardwareHostFriendlyName ;
HardwareDto* HardwareList[20];
inline HardwareHostDto(int HardwareHostID, int BranchID, string HardwareHostFriendlyName, HardwareDto* HardwareList[20])
this->HardwareHostID = HardwareHostID;
this->BranchID = BranchID;
this->HardwareHostFriendlyName = HardwareHostFriendlyName;
this->HardwareList[0] = HardwareList[0];
;
和
HardwareDto *HardwareList[20];
是我的全局hardwaredto 列表。在这个例子中,我只在这个列表中插入了一个 hardwarehostdto 对象。
我正在尝试通过 boost 函数对其进行序列化:
std::ofstream ofs("filename.xml");
unsigned int flags = boost::archive::no_header;
boost::archive::xml_iarchive ia(is, boost::archive::no_header);
boost::archive::xml_oarchive oa(ofs, flags);
HardwareHostDto* HardwareHost = new HardwareHostDto(1, 1, "kiosk", HardwareList);
oa << BOOST_SERIALIZATION_NVP(HardwareHost);
这段代码执行后,我得到了这个filename.xml:
<HardwareHost class_id="0">
<HardwareHostID>1</HardwareHostID>
<BranchID>1</BranchID>
<HardwareHostFriendlyName>kiosk</HardwareHostFriendlyName>
<HardwareList>
<count>20</count>
<item class_id="1">
<HardwareID>2</HardwareID>
<HardwareHostID>2</HardwareHostID>
<HardwareFriendlyName>Ankara</HardwareFriendlyName>
</item>
</HardwareList>
</HardwareHost>
<item>
标签应该是<Hardware>
,但我无法更改它。
我的问题是:有没有办法改变<item>
标签,或者actullay 自定义这个xml 结构,比如没有<count>
标签或标志?我在 boost 网站上找到了一些方法,但无法处理。
谢谢。
【问题讨论】:
为什么要存储指针并动态分配?您的代码现在会泄漏内存。 问题是关于提升的。您的代码和所有答案都包含 Boost Serialization。最好保留标签。 【参考方案1】:是的,你可以破解它。也许。在一定程度上。见:
how to customise default Boost xml Serialisation default node naming to make it more readable How to use Boost XML Parser deserialization issue after changing namespace to customise tag names for boost xml不,你不应该。使用 XML 库编写任意 XML。
Boost Serialization 只做序列化。归档格式是实现细节。
不好的例子
不该做什么(它破坏了非默认的可构造类型,它破坏了版本控制)。
从好的方面来说,这段代码不会泄漏内存。
Live On Coliru
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/vector.hpp>
class HardwareDto
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned)
ar &BOOST_SERIALIZATION_NVP(HardwareID);
ar &BOOST_SERIALIZATION_NVP(HardwareHostID);
ar &BOOST_SERIALIZATION_NVP(HardwareFriendlyName);
public:
int HardwareHostID;
int HardwareID;
std::string HardwareFriendlyName;
HardwareDto(int HardwareHostID = -1, int HardwareID = -1, std::string HardwareFriendlyName = )
: HardwareHostID(HardwareHostID),
HardwareID(HardwareID),
HardwareFriendlyName(HardwareFriendlyName)
;
using HardwareDtoList = std::vector<HardwareDto>;
class HardwareHostDto
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned)
ar &BOOST_SERIALIZATION_NVP(HardwareHostID);
ar &BOOST_SERIALIZATION_NVP(BranchID);
ar &BOOST_SERIALIZATION_NVP(HardwareHostFriendlyName);
ar &BOOST_SERIALIZATION_NVP(HardwareList);
public:
int HardwareHostID;
int BranchID;
std::string HardwareHostFriendlyName;
HardwareDtoList HardwareList;
HardwareHostDto(int HardwareHostID, int BranchID, std::string HardwareHostFriendlyName, HardwareDtoList HardwareList)
: HardwareHostID(HardwareHostID),
BranchID(BranchID),
HardwareHostFriendlyName(HardwareHostFriendlyName),
HardwareList(HardwareList)
;
namespace boost namespace serialization
template <typename Ar>
void serialize(Ar& ar, std::vector<HardwareDto>& v, unsigned)
size_t count = v.size();
ar & BOOST_SERIALIZATION_NVP(count);
v.resize(count);
for (auto& el : v)
ar & boost::serialization::make_nvp("Hardware", el);
#include <fstream>
#include <iostream>
int main()
unsigned int flags = boost::archive::no_header;
HardwareDtoList HardwareList;
HardwareList.emplace_back(1, 2, "friendly");
std::ofstream ofs("filename.xml");
boost::archive::xml_oarchive oa(ofs, flags);
HardwareHostDto host(1, 1, "kiosk", HardwareList);
oa << boost::serialization::make_nvp("HardwareHost", host);
HardwareHostDto roundtrip(-1, -1, "", );
std::ifstream ifs("filename.xml");
boost::archive::xml_iarchive ia(ifs, flags);
ia >> boost::serialization::make_nvp("HardwareHost", roundtrip);
std::cout << "Read back: " << roundtrip.HardwareHostID << "\n";
std::cout << "Read back: " << roundtrip.BranchID << "\n";
std::cout << "Read back: " << roundtrip.HardwareHostFriendlyName << "\n";
for (auto& h: roundtrip.HardwareList)
std::cout << "Item: " << h.HardwareID << ", " << h.HardwareHostID << ", " << h.HardwareFriendlyName << "\n";
打印
Read back: 1
Read back: 1
Read back: kiosk
Item: 2, 1, friendly
并将 XML 写为:
<HardwareHost class_id="0" tracking_level="0" version="0">
<HardwareHostID>1</HardwareHostID>
<BranchID>1</BranchID>
<HardwareHostFriendlyName>kiosk</HardwareHostFriendlyName>
<HardwareList class_id="1" tracking_level="0" version="0">
<count>1</count>
<Hardware class_id="2" tracking_level="0" version="0">
<HardwareID>2</HardwareID>
<HardwareHostID>1</HardwareHostID>
<HardwareFriendlyName>friendly</HardwareFriendlyName>
</Hardware>
</HardwareList>
</HardwareHost>
做什么
使用 XML 库。这需要(很多)泛化,但这里是使用 PugiXML 的开始:
Live On Coliru
#include <pugixml.hpp>
#include <iostream>
#include <vector>
struct HardwareDto
int HardwareHostID;
int HardwareID;
std::string HardwareFriendlyName;
;
struct HardwareHostDto
int HardwareHostID;
int BranchID;
std::string HardwareHostFriendlyName;
std::vector<HardwareDto> HardwareList;
;
struct Xml
struct Saver
template <typename T>
void operator()(pugi::xml_node parent, std::string const& name, T const& value) const
auto node = named_child(parent, name);
node.text().set(to_xml(value));
void operator()(pugi::xml_node parent, std::string const& name, HardwareDto const& o) const
auto dto = named_child(parent, name);
operator()(dto, "HardwareHostID", o.HardwareHostID);
operator()(dto, "HardwareID", o.HardwareID);
operator()(dto, "HardwareFriendlyName", o.HardwareFriendlyName);
template <typename C>
void operator()(pugi::xml_node parent, std::string const& name, std::string const& item_name, C const& container) const
auto list = named_child(parent, name);
for (auto& item : container)
operator()(list, item_name, item);
void operator()(pugi::xml_node parent, std::string const& name, HardwareHostDto const& o) const
auto dto = named_child(parent, name);
operator()(dto, "HardwareHostID", o.HardwareHostID);
operator()(dto, "BranchID", o.BranchID);
operator()(dto, "HardwareHostFriendlyName", o.HardwareHostFriendlyName);
operator()(dto, "HardwareList", "Hardware", o.HardwareList);
private:
// serialization
template <typename T> static T const& to_xml(T const& v) return v;
static char const* to_xml(std::string const& v) return v.c_str();
pugi::xml_node named_child(pugi::xml_node parent, std::string const& name) const
auto child = parent.append_child();
child.set_name(name.c_str());
return child;
;
struct Loader
void operator()(pugi::xml_node parent, std::string const& name, std::string& value) const
auto node = parent.first_element_by_path(name.c_str());
value = node.text().as_string();
void operator()(pugi::xml_node parent, std::string const& name, int& value) const
auto node = parent.first_element_by_path(name.c_str());
value = node.text().as_int();
void operator()(pugi::xml_node dto, HardwareDto& o) const
operator()(dto, "HardwareHostID", o.HardwareHostID);
operator()(dto, "HardwareID", o.HardwareID);
operator()(dto, "HardwareFriendlyName", o.HardwareFriendlyName);
void operator()(pugi::xml_node parent, std::string const& name, HardwareDto& o) const
auto dto = parent.first_element_by_path(name.c_str());
operator()(dto, "HardwareHostID", o.HardwareHostID);
operator()(dto, "HardwareID", o.HardwareID);
operator()(dto, "HardwareFriendlyName", o.HardwareFriendlyName);
template <typename C>
void operator()(pugi::xml_node parent, std::string const& name, std::string const& item_name, C& container) const
auto list = parent.first_element_by_path(name.c_str());
for (auto& node : list)
if (node.type() != pugi::xml_node_type::node_element)
std::cerr << "Warning: unexpected child node type ignored\n";
continue;
if (node.name() != item_name)
std::cerr << "Warning: unexpected child node ignored (" << node.name() << ")\n";
continue;
container.emplace_back();
operator()(node, container.back());
void operator()(pugi::xml_node dto, HardwareHostDto& o) const
operator()(dto, "HardwareHostID", o.HardwareHostID);
operator()(dto, "BranchID", o.BranchID);
operator()(dto, "HardwareHostFriendlyName", o.HardwareHostFriendlyName);
operator()(dto, "HardwareList", "Hardware", o.HardwareList);
void operator()(pugi::xml_node parent, std::string const& name, HardwareHostDto& o) const
operator()(parent.first_element_by_path(name.c_str()), o);
;
;
int main()
pugi::xml_document _doc;
Xml::Saver saver;
HardwareHostDto host = 1, 1, "kiosk", 1, 2, "friendly" ;
saver(_doc.root(), "HardwareHost", host);
_doc.save_file("test.xml");
HardwareHostDto roundtrip;
pugi::xml_document _doc;
_doc.load_file("test.xml");
Xml::Loader loader;
loader(_doc.root(), "HardwareHost", roundtrip);
std::cout << "Read back: " << roundtrip.HardwareHostID << "\n";
std::cout << "Read back: " << roundtrip.BranchID << "\n";
std::cout << "Read back: " << roundtrip.HardwareHostFriendlyName << "\n";
for (auto& h: roundtrip.HardwareList)
std::cout << "Item: " << h.HardwareID << ", " << h.HardwareHostID << ", " << h.HardwareFriendlyName << "\n";
也可以打印
Read back: 1
Read back: 1
Read back: kiosk
Item: 2, 1, friendly
并写一个test.xml
:
<?xml version="1.0"?>
<HardwareHost>
<HardwareHostID>1</HardwareHostID>
<BranchID>1</BranchID>
<HardwareHostFriendlyName>kiosk</HardwareHostFriendlyName>
<HardwareList>
<Hardware>
<HardwareHostID>1</HardwareHostID>
<HardwareID>2</HardwareID>
<HardwareFriendlyName>friendly</HardwareFriendlyName>
</Hardware>
</HardwareList>
</HardwareHost>
【讨论】:
添加了hacking serialization 和using an Xml Library 的示例 为了绝对矫枉过正,这里有一个小的 repo,它显示了 Pugi 的东西是如何被概括的。 github.com/sehe/SimpleSerial 我将序列化接口拆分为tiny frontend 和several backends (PugiXml, Boost Property Tree (xml/json) and Libxml2 implemented)。该测试在所有后端运行相同的往返测试:github.com/sehe/SimpleSerial/blob/libxml2-prototype/main.cpp 你太棒了。感谢您的大力支持。抱歉,我刚刚设法理解并编译了代码中的内容,因此回复迟了。谢谢您,先生,请拿走您的饼干! 有什么办法可以删除"" ? @MarijkeBuurlage 请。不。这使它成为 not-XML。为什么你认为你需要它? (另见例如***.com/questions/47448864/…)【参考方案2】:为了完全自定义 boost 序列化的 XML 输出,您可以编写自己的 XML 存档。这是一个三步过程:
-
定义一个编写原始类型的类,比如
TextOPrimitiveOnXML
。
定义一个编写复合类型的类:BasicXMLOArchive
。这是将编写 XML 标记的类。
将所有内容粘贴到XMLOArchive
。
优点是不仅可以重复使用serialize
代码,还可以使用不同的后端。例如,您可以拥有一个立即创建 DOM 而不仅仅是文本输出的存档。
这是我提到的类的骨架。
TextOPrimitiveOnXML
,它的save
方法定义了原始类型的写入。
class TextOPrimitiveOnXML
protected:
TextOPrimitiveOnXML()
template <class T>
void save(const T & t)
void save(const bool t)
void save(const signed char t)
void save(const unsigned char t)
void save(const char t)
void save(const wchar_t t)
void save(const char *t)
void save(const wchar_t *t)
void save(const std::string &t)
void save(const std::wstring &t)
void save_binary(const void *address, std::size_t count);
;
BasicXMLOArchive
,这是你操作标签和属性的地方:
template <class Archive>
class BasicXMLOArchive :
public boost::archive::detail::common_oarchive<Archive>
friend class boost::archive::detail::interface_oarchive<Archive>;
friend class boost::archive::save_access;
typedef boost::archive::detail::common_oarchive<Archive> base;
protected:
void init()
BasicXMLOArchive(unsigned int flags)
: base(flags)
template <class T>
void save_override(T & t, int)
// If your program fails to compile here, its most likely due to
// not specifying an nvp wrapper around the variable to
// be serialized.
BOOST_MPL_ASSERT((boost::serialization::is_wrapper<T>));
this->base::save_override(t, 0);
// special treatment for name-value pairs.
template <class T>
void save_override(const boost::serialization::nvp<T> &t, int)
void save_override(const boost::archive::object_id_type & t);
void save_override(const boost::archive::object_reference_type & t);
void save_override(const boost::archive::version_type & t);
void save_override(const boost::archive::class_id_type & t);
void save_override(const boost::archive::class_id_optional_type & t);
void save_override(const boost::archive::class_id_reference_type & t);
void save_override(const boost::archive::class_name_type & t);
void save_override(const boost::archive::tracking_type & t);
public:
boost::archive::library_version_type get_library_version() const
return boost::archive::library_version_type(0);
;
最后的胶水:
template <class Archive>
class XMLOArchiveImpl :
public TextOPrimitiveOnXML
, public BasicXMLOArchive<Archive>
friend class boost::archive::save_access;
public:
XMLOArchiveImpl(unsigned int flags)
: TextOPrimitiveOnXML()
, BasicXMLOArchive<Archive>(flags)
init();
void save_binary(const void *address, std::size_t count)
this->TextOPrimitiveOnXML::save_binary(address, count);
;
class XMLOArchive :
public XMLOArchiveImpl<XMLOArchive>
friend class BasicXMLOArchive<XMLOArchive>;
public:
XMLOArchive(unsigned int flags)
: XMLOArchiveImpl(flags)
;
【讨论】:
谢谢你的回答,但上一个答案对我来说更好。 没问题。我实际上会结合这两个答案并编写一个使用 PugiXML 作为后端的 boost 存档。我喜欢提升 :)。 是的,你的回答真的很棒。但是另一个也教我们如何更好地序列化列表对象。谢谢你的回答先生。以上是关于如何在 boost 序列化中更改或删除标签?的主要内容,如果未能解决你的问题,请参考以下文章