现在的位置: 首页 > 综合 > 正文

Easier XML With Boost

2014年01月15日 ⁄ 综合 ⁄ 共 4105字 ⁄ 字号 评论关闭

转自: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
    .

抱歉!评论已关闭.