14.9. Применение XML для сохранения и восстановления набора объектов
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 ";
return EXIT_FAILURE;
}
} catch (const exception& e) {
cout << e.what() << " ";
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">
<animalList class_id="0" tracking_level ="0" version="0">
<count>3</count>
<item class_id="1" tracking_level="0" version="0">
<name>Herby</name>
<species>elephant</species>
<dateOfBirth class_id="2" tracking_level="0" version="0">
<date>19920423</date>
</dateOfBirth>
<veterinarian class_id="3" tracking_level="0" version="0">
<name>Dr. Hal Brown</name>
<phone>(801)595-9627</phone>
</veterinarian>
<trainer>
<name>Bob Fisk</name>
<phone>(801)881-2260</phone>
</trainer>
</item>
<item>
<name>Sheldon</name>
<species>parrot</species>
<dateOfBirth>
<date>19980930</date>
</dateOfBirth>
<veterinarian>
<name>Dr. Kevin Wilson</name>
<phone>(801)466-6498</phone>
</veterinarian>
<trainer>
<name>Eli Wendel</name>
<phone>(801)929-2506</phone>
</trainer>
</item>
<item>
<name>Dippy</name>
<species>penguin</species>
<dateOfBirth>
<date>20010608</date>
</dateOfBirth>
<veterinarian>
<name>Dr. Barbara Swayne</name>
<phone>(801)459-7746</phone>
</veterinarian>
<trainer>
<name>Ben Waxman</name>
<phone>(801)882-3549</phone>
</trainer>
</item>
</animalList>
Обсуждение
Библиотека Boost Serialization обеспечивает наиболее изощренный и гибкий способ сохранения и восстановления объектов C++. Она представляет собой очень сложный фреймворк. Например, она позволяет сериализовать сложные структуры данных, содержащие циклические ссылки и указатели на полиморфные объекты. Более того, применение этой библиотеки совсем не ограничивается сериализацией XML: кроме архивов XML она предоставляет несколько типов текстовых и бинарных архивов. Архивы XML и текстовые архивы являются переносимыми, т.е. данные можно сериализовать в одной системе и десериализовать в другой; бинарные архивы не переносимы, но компактны.
Нет никаких спецификаций, которым соответствовали бы документы XML, полученные при помощи Boost.Serialization, и их формат может изменяться в новых версиях Boost. Поэтому вы не можете использовать эти документы совместно с другими фреймворками сериализации С++. Тем не менее XML-сериализация приносит пользу, потому что сериализованный вывод легко воспринимается человеком и может обрабатываться инструментальными средствами, ориентированными на XML.
Примеры 14.25 и 14.26 демонстрируют интрузивную сериализацию (intrusive serialization): классы Animal и Contact были модифицированы, чтобы обеспечить их сериализацию. Boost.Serialization также поддерживает неинтрузивную сериализацию (nonintrusive serialization), обеспечивая сериализацию классов без модификации их определений при условии доступности всех состояний объекта через его открытый интерфейс. Вы уже видели пример неинтрузивной сериализации в примере 14.27: шаблон std::vector допускает сериализацию, несмотря на то что его определение не может модифицироваться конечными пользователями. Фактически все контейнеры стандартной библиотеки являются сериализуемыми; для обеспечения сериализации контейнера, определенного в стандартном заголовочном файле xxx, просто включите заголовочный файл boost/serialization/xxx.hpp. Дополнительную информацию о неинтрузивной сериализации вы можете найти в документации Boost.Serialization.
Примеры 14.25 и 14.26 иллюстрируют также двойственную роль оператора &: он действует как оператор << при сериализации объекта и как оператор >> при десериализации объекта. Это удобно, потому что позволяет реализовать сериализацию и десериализацию одной функцией. Однако в некоторых случаях неудобно использовать одну функцию для сериализации и десериализации; для этого в Boost.Serialization предусмотрен механизм разделения метода serialize() на два отдельных метода, load() и save(). Если вам необходимо воспользоваться преимуществами этой возможности, обратитесь к документации Boost.Serialization.
В примерах 14.25, 14.26 и 14.27 я использую функцию boost::serialization::make_nvp для конструирования пар вида «имя-значение». В Boost.Serialization предусмотрен также макрос BOOST_SERIALIZATION_NVP, который позволяет выполнять сериализацию переменной, указывая ее имя. Первый компонент пары будет сконструирован автоматически препроцессором, используя оператор «стрингизации» (stringizing) # для преобразования макропараметров в строковые константы.
// То же самое, что и ar & make_nvp("name_", name_);
ar & BOOST_SERIALIZATION_NVP(name_);
В этих примерах я использую make_nvp вместо BOOST_SERIALIZATION_NVP для лучшего контроля имен тегов, чтобы содержимое архива XML легче читалось.
В документации Boost.Serialization рекомендуется объявлять метод serialize() как закрытый (private) для уменьшения ошибок пользователя, когда добавляется поддержка сериализации в классы, производные от других сериализуемых классов. Для того чтобы библиотека Boost.Serialization могла вызвать метод serialize() вашего класса, вам необходимо объявить дружественным класс boost::serialization::access.
Наконец второй параметр метода serialize() в примерах 14.25 и 14.26 относится к той части Boost.Serialization, которая поддерживает управление версиями классов (class versioning). Когда объект определенного класса первый раз сохраняется в архиве, вместе с ним сохраняется также его версия; когда выполняется десериализация экземпляра класса. Boost.Serialization передает сохраненную версию методу serialize в качестве второго аргумента. Эта информация может использоваться для специализации десериализации; например, serialize мог бы загружать переменную-член только в том случае, если записанная в архив версия класса, по крайней мере, не меньше версии класса, первым объявившим эту переменную. По умолчанию класс имеет версию 0. Для задания версии класса вызовите макрос BOOST_CLASS_VERSION, который определен в заголовочном файле boost/serialization/version.hpp, передавая в качестве аргументов имя и версию класса.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Диалоговое окно открытия и сохранения файла
Диалоговое окно открытия и сохранения файла Windows Me/2000 имеет новое диалоговое окно "ОткрытьСохранить файл", которое можно изменить по Вашему желанию. В левой части диалогового окна расположены пять системных папок, которые можно поменять местами или изменить на те,
Диалоговое окно открытия и сохранения файла
Диалоговое окно открытия и сохранения файла Windows XP имеет новое диалоговое окно "ОткрытьСохранить файл", которое можно изменить по Вашему желанию. В левой части диалогового окна расположены пять системных папок, которые можно поменять местами или изменить на те, которыми
Запрет сохранения паролей в Dial-Up-соединениях
Запрет сохранения паролей в Dial-Up-соединениях По умолчанию, в Dial-Up-соединениях введенный пароль сохраняется после успешного соединения, если задействована опция "Сохранять имя пользователя и пароль", расположенная на диалоговом окне для Dial-Up. Это достаточно удобно для
Настройка параметров открытия и сохранения файлов
Настройка параметров открытия и сохранения файлов Вкладка открытия и сохранения файлов Open and Save диалогового окна Options показана на рис. 3.5. Рис. 3.5. Диалоговое окно настройки параметров открытия и сохранения файловНа ней осуществляется настройка следующих параметров.• В
Добавление и удаление объектов из набора
Добавление и удаление объектов из набора Выбирая новые объекты каким-либо способом в ответ на приглашение Select objects:, мы добавляем их к уже выделенным. Так происходит, пока не будет нажата клавиша Enter. Однако кроме добавления объектов в набор выделения мы можем исключить
Программы для сохранения и восстановления конфигурации
Программы для сохранения и восстановления конфигурации В предыдущем разделе мы уже горестно вздыхали по поводу того прискорбного факта, что операционная система – вещь крайне ненадежная и уязвимая. И даже называли ее ахиллесову пяту – системный реестр. Помимо
Настройка параметров открытия и сохранения файлов
Настройка параметров открытия и сохранения файлов Вкладка открытия и сохранения файлов Open and Save диалогового окна Options показана на рис. 3.5. На ней осуществляется настройка следующих параметров. Рис. 3.5. Диалоговое окно настройки параметров открытия и сохранения
Пользовательские точки сохранения
Пользовательские точки сохранения Операторы пользовательских точек сохранения (user savepoints), также называемые вложенными транзакциями, позволяют вам "упаковать" группы операций внутри транзакции и отмечать их, если пересылка в базу данных была успешной. Если позже в
"Точки сохранения" в PSQL
"Точки сохранения" в PSQL Добавление возможностей создания пользовательских точек сохранения в Firebird 1.5 позволяет приложению управлять область действия отката транзакции. В PSQL всегда была возможность обработки исключений. Подробности см. в главе
Вложенные исключения в качестве точек сохранения
Вложенные исключения в качестве точек сохранения Вложенная архитектура блоков выполнения модулей PSQL означает, конечно, что PSQL поддерживает "вложенные" транзакции. Деятельность каждого модуля PSQL выполняется в контексте той транзакции, в которой он был вызван.
Настройка параметров открытия и сохранения файлов
Настройка параметров открытия и сохранения файлов Вкладка открытия и сохранения файлов Open and Save диалогового окна Options показана на рис. 3.5. Рис. 3.5. Диалоговое окно настройки параметров открытия и сохранения файловНа ней осуществляется настройка следующих параметров. • В
Варианты сохранения проекта
Варианты сохранения проекта По умолчанию проект ArchiCAD сохраняется в формате PLN, но при этом в файл проекта записываются только ссылки на использованные в проекте библиотечные объекты, текстуры и фоновые рисунки. По этой причине при переносе файла проекта на другой
Добавление и удаление объектов из набора
Добавление и удаление объектов из набора Выбирая новые объекты каким-либо способом в ответ на приглашение Select objects:, мы добавляем их к выделенным объектам. Так происходит, пока не будет нажата клавиша Enter. Однако, кроме добавления объектов в набор выделения, мы можем и
Окно открытия или сохранения файла
Окно открытия или сохранения файла В этом разделе мы рассмотрим, как с помощью системного реестра можно изменять режимы работы в окне открытия/сохранения файла.Поле кнопок окна открытия/сохранения файловНекоторые стандартные программы Windows Vista используют старое окно,
13.4.5. Автоматическое сохранение. Параметры сохранения
13.4.5. Автоматическое сохранение. Параметры сохранения У вас частенько отключают электропитание, источник бесперебойного питания еще не купили, но зато постоянно забываете сохранить документ? История знакомая. Вам может помочь сам Word — просто настройте автоматическое
Дмитрий Шабанов: "Инстинкт сохранения вида"?
Дмитрий Шабанов: "Инстинкт сохранения вида"? Автор: Дмитрий ШабановОпубликовано 07 июня 2012 годаНекий йоговский сайт ничтоже сумняшеся утверждает: "Человек, как и любое другое существо на Земле, появляется для того, чтобы обеспечить процветание и развитие своего вида".