Easier XML With Boost

转自: http://www.fnord.ca/articles/xml.html

Introduction

As developers, we often want to save data from an object into a file with an XML based data format. The principle of loose coupling suggests that each object shouldn't produce its own XML code directly; there's no need for every class to know about XML. Even using an abstract representation of the XML format, such as a DOM tree, requires the class to know too much about XML. Also, writing XML is unnecessary low-level work for the developer. It's better to save to a simple, completely abstract format, and move elsewhere the details of how to save that format to XML. The Boost Serialization and Archive libraries allow this.

Serialization

To add serialization support to a class, you write a serialize() method that describes in what order the data fits into a generic archive. Here's an example:

#include <list>
#include <string>
#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/nvp.hpp>

class italian_sandwich
{
    public:
        italian_sandwich();

    private:
        string m_bread, m_cheese;
        list<string> m_meats;
        bool m_spicy_eggplant_p;

    friend class boost::serialization::access;
    template<class archive>
    void serialize(archive& ar, const unsigned int version)
    {
        using boost::serialization::make_nvp;
        ar & make_nvp("Bread", m_bread);
        ar & make_nvp("Cheese", m_cheese);
        ar & make_nvp("Meats", m_meats);
        ar & make_nvp("Add Spicy Eggplant", m_spicy_eggplant_p);
    }
}


 

Notes:

  • The & operator becomes << when saving and >> when loading.
  • make_nvp() assigns a name to each element we want to save.
  • The unused version argument could be used later if we change the class.

Note how we didn't need to manually descend into the list of strings (m_meats) and serialize each string individually. As long as a type is serializable, STL containers of that type are serialized automatically. Similarly, if we built serialization support for one of our own classes, say a bread_t class, then we could still serialize the m_bread attribute with the same simple code, instead of manually descending into the m_bread object.

Archive

To save/load a serializable object to/from an XML file, we create a file stream, initialize an XML archive with that stream, and use the << or >> operator to write the object out to the archive or read it in. Here's an example using italian_sandwich:

#include <base/file_stream.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/nvp.hpp>

void save_sandwich(const italian_sandwich& sw, const string& file_name)
{
    typedef base::file_stream bafst;
    bafst::file_stream ofs(file_name, bafst::trunc | bafst::out);
    boost::archive::xml_oarchive xml(ofs);
    xml << boost::serialization::make_nvp("Italian Sandwich", sw);
}

italian_sandwich load_sandwich(const string& file_name)
{
    typedef base::file_stream bafst;
    italian_sandwich sw;
    bafst::file_stream ifs(file_name, bafst::binary | bafst::in);
    boost::archive::xml_oarchive xml(ifs);
    xml >> boost::serialization::make_nvp("Italian Sandwich", sw);
}


 

Notes:

  • Exception handling code is needed around the file_stream and archive initialization.
  • make_nvp() is not strictly necessary.

Further Examples

italian_sandwich was a very simple example class. It is easy to handle more complicated cases, for example:

  • If we can't modify a class directly, we can add a free serialization function outside it that uses only public methods. Its signature would look like:
    template<class archive>
    void serialize(archive& ar, italian_sandwich& sw, const unsigned int version);


    If the class has a method to save/load a data attribute (eg. for safety-checking), then the same code won't work for loading or saving to the archive. In that case we can split the serialize() method into save() and load():

    #include "bread_t.hpp"
    #include <boost/serialization/nvp.hpp>
    #include <boost/serialization/split_member.hpp>
    
    template<class archive>
    void load(archive& ar, const unsigned int version)
    {
        bread_t bread;
        ar & boost::serialization::make_nvp("Bread", bread);
        set_bread(bread);
        // load other ingredients...
    }
    
    template<class archive>
    void save(archive& ar, const unsigned int version)
    {
        const bread_t bread = get_bread();
        ar & boost::serialization::make_nvp("Bread", bread);
        // save other ingredients...
    }
    
    // define serialize() using save() and load()
    BOOST_SERIALIZATION_SPLIT_MEMBER();


    There are other archive formats, such as human readable text or binary formats. Once a class has serialization support, we can use any of those archive formats without changing the class.

    Conclusion

    Hopefully these two Boost libraries can prevent us from writing some unnecessary code.  For further reference, see the Boost documentation page.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值