Выбрать главу
Смотри также

Рецепт 14.7.

14.9. Применение XML для сохранения и восстановления набора объектов

Проблема

Требуется иметь возможность сохранения набора объектов C++ в документе XML и считывания их потом обратно в память.

Решение

Используйте библиотеку Boost Serialization. Эта библиотека позволяет сохранять и восстанавливать объекты, используя классы, называемые архивами. Для использования этой библиотеки вы должны сначала сделать каждый из ваших классов сериализуемым (serializable), что просто означает возможность записи экземпляров класса в архив (это называется сериализацией) и их обратного считывания в память (это называется десериализацией). Затем на этапе выполнения вы можете сохранить ваши объекты в архиве XML, используя оператор <<, и восстановить их, используя оператор >>.

Чтобы сделать класс сериализуемым, добавьте шаблон функции-члена serialize со следующей сигнатурой.

template<typename Archive>

void serialize(Archive& ar, const unsigned int version);

В реализации serialize необходимо обеспечить запись каждого данного-члена класса в указанный архив в виде пары «имя-значение», используя оператор &. Например, если вы хотите сериализовать и десериализовать экземпляры класса Contact из примера 14.2, добавьте функцию-член serialize, как это сделано в примере 14.25.

Пример 14.25. Добавление поддержки сериализации в класс Contact из примера 14.2

#include <boost/serialization/nvp.hpp> // пара "имя-значение"

class Contact {

 ...

private:

 friend class boost::serialization::access;

 template<typename Archive>

 void serialize(Archive& ar, const unsigned int version) {

  // Записать (или считать) каждое данное-член в виде пары имя-значение

  using boost::serialization::make_nvp;

  ar & make_nvp("name", name_);

  ar & make_nvp("phone", phone_);

 }

...

};

Аналогично можно обеспечить сериализацию класса Animal из примера 14.2, как это сделано в примере 14.26.

Пример 14.26. Добавление поддержки сериализации для класса Animal из примера 14.2

...

// Включить поддержку сериализации для boost::gregorian::date

#include <boost/date_time/gregorian/greg_serialize.hpp>

...

class Contact {

 ...

private:

 friend class boost::serialization::access;

 template<typename Archive>

 void serialize(Archive& ar, const unsigned int version) {

  // Записать (или считать) каждое данное-член в виде пары имя-значение

  using boost::serialization::make_nvp;

  ar & make_nvp("name", name_);

  ar & make_nvp("species", species_);

  ar & make_nvp("dateOfBirth", dob_);

  ar & make_nvp("veterinarian", vet_);

  ar & make_nvp("trainer", trainer_);

 }

 ...

};

Теперь вы можете сериализовать Animal, создавая архив XML типа boost::archive::xml_oarchive и записывая информацию о животном в архив, используя оператор <<. Конструктор xml_oarchive в качестве аргумента принимает std::ostream; часто этим аргументом будет поток вывода, используемый для записи в файл, однако в общем случае для записи данных может использоваться ресурс любого типа. После сериализации экземпляра Animal его можно считать обратно в память, конструируя архив XML типа boost::archive::xml_iarchive, подключая его к тому же самому ресурсу, который использовался первым архивом, и применяя оператор >>.

Пример 14.27 показывает, как можно использовать Boost.Serialization для сохранения вектора std::vector, состоящего из объектов Animal, в файле animals.xml и затем для загрузки его обратно в память. В примере 14.28 показано содержимое файла animals.xml после выполнения программы из примера 14.27.

Пример 14.27 Сериализация вектора std::vector, состоящего из объектов Animal

#include <fstream>

#include <boost/archive/xml_oarchive.hpp> // Архив для записи XML

#include <boost/archive/xml_iarchive.hpp> // Архив для чтения XML

#include <boost/serialization/vector.hpp> // Средства сериализации вектора

#include "animal.hpp" // std::vector

int main() {

 using namespace std;

 using namespace boost::archive;       // пространство имен для архивов

 using namespace boost::serialization; // пространство имен для make_nvp

 try {

  // Заполнить список животных

  vector<Animal> animalList;

  animalList.push_back(

   Animal("Herby", "elephant", "1992-04-23",

   Contact("Dr. Hal Brown", "(801)595-9627"),

   Contact("Bob Fisk", "(801)881-2260")));

  animalList.push_back(

   Animal("Sheldon", "parrot", "1998-09-30",

   Contact("Dr. Kevin Wilson", "(801)466-6498"),

   Contact("Eli Wendel", "(801)929-2506")));

  animalList.push_pack(

   Animal("Dippy", "penguin", "2001-06-08",

   Contact("Dr. Barbara Swayne", "(801)459-7746"),

   Contact("Ben Waxman", "(801)882-3549")));

  // Сконструировать выходной архив XML и сериализовать список

  ofstream fout("animals.xml");

  xml_oarchive oa(fout);

  oa << make_nvp("animalList", animalList);

  fout.close();

  // Сконструировать входной архив XML и десериализовать список

  ifstream fin("animals.xml");

  xml_iarchive ia(fin);

  vector<Animal> animalListCopy;

  ia >> make_nvp("animalList", animalListCopy);

  fin.close();

  if (animalListCopy != animalList) {

   cout << "XML serialization failed\n";

   return EXIT_FAILURE;

  }

 } catch (const exception& e) {

  cout << e.what() << "\n";

  return EXIT_FAILURE;

 }

}

Пример 14.28. Файл animals.xml после выполнения программы из примера 14.27

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>

<!DOCTYPE boost_serialization>

<boost_serialization signature="serialization::archive" version="3">