Yes, you can hack it. Maybe. To an extent. See:
And no, you shouldn't. Use an XML library to write arbitrary XML.
Boost Serialization only does serialization. The archive format is implementation detail.
Bad Example
What not to do (it breaks non-default constructible types, it breaks versioning).
On the bright side, this code doesn't leak memory.
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";
}
}
}
Prints
Read back: 1
Read back: 1
Read back: kiosk
Item: 2, 1, friendly
And writes the XML as:
<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>
What To Do
Using an XML library. This calls for (a lot) generalization, but here's a start using 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";
}
}
}
Which also prints
Read back: 1
Read back: 1
Read back: kiosk
Item: 2, 1, friendly
And writes a 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>