【问题标题】:How to change or delete tags in boost serialization?如何在 boost 序列化中更改或删除标签?
【发布时间】: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>

&lt;item&gt; 标签应该是&lt;Hardware&gt;,但我无法更改它。 我的问题是:有没有办法改变&lt;item&gt; 标签,或者actullay 自定义这个xml 结构,比如没有&lt;count&gt; 标签或标志?我在 boost 网站上找到了一些方法,但无法处理。

谢谢。

【问题讨论】:

  • 为什么要存储指针并动态分配?您的代码现在会泄漏内存。
  • 问题是关于提升的。您的代码和所有答案都包含 Boost Serialization。最好保留标签。

标签: c++ xml boost xml-serialization


【解决方案1】:

是的,你可以破解它。也许。在一定程度上。见:

不,你不应该。使用 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>

【讨论】:

【解决方案2】:

为了完全自定义 boost 序列化的 XML 输出,您可以编写自己的 XML 存档。这是一个三步过程:

  1. 定义一个编写原始类型的类,比如TextOPrimitiveOnXML
  2. 定义一个编写复合类型的类:BasicXMLOArchive。这是将编写 XML 标记的类。
  3. 将所有内容粘贴到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 存档。我喜欢提升 :)。
  • 是的,你的回答真的很棒。但是另一个也教我们如何更好地序列化列表对象。谢谢你的回答先生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-12
  • 1970-01-01
  • 2016-03-20
  • 2020-11-23
  • 1970-01-01
相关资源
最近更新 更多