Табл. 14.1. Библиотеки C++ для XML
| Имя библиотеки |
Домашняя страница |
| TinyXml |
www.grinninglizard.com/tinyxml |
| Xerxes |
xml.apache.crg/xerces-c |
| Xalan |
xml.apache.org/xalan-c |
| Pathan 1 |
software.decisionsoft.com/pathanIntro.html |
| Boost.Serialization |
www.boost.org/libs/serialization |
Табл. 14.2. Назначение библиотек
| Имя библиотеки |
Назначение |
Рецепты |
| TinyXml |
DOM (нестандартная версия) |
Рецепт 14.1 |
| Xerxes |
SAX2, DOM, XML Schema |
Рецепты 14.2-14.8 |
| Xalan |
XSLT, XPath |
Рецепты 14.7-14.8 |
| Pathan |
XPath |
Рецепт 14.8 |
| Boost.Serialization |
Сериализация XML |
Рецепт 14.9 |
14.1. Синтаксический анализ простого документа XML
Проблема
Имеется некоторая совокупность данных, хранимых в документе XML. Требуется выполнить синтаксический анализ документа и превратить эти данные в объекты C++. Документ XML имеет достаточно небольшой размер и может поместиться в оперативной памяти, причем в документе не используется внутреннее определение типа документа (Document Type Definition — DTD) и отсутствуют пространства имен XML.
Решение
Используйте библиотеку TinyXml. Во-первых, определите объект типа TiXmlDocumentи вызовите его метод LoadFile(), передавая полное имя файла вашего XML-документа в качестве его аргумента. Если LoadFile()возвращает значение «true», то это означает, что анализ вашего документа завершился успешно. В этом случае вызовите метод RootElement()для получения указателя на объект типа TiXmlElement, представляющего корневой элемент документа. Этот объект имеет иерархическую структуру, которая соответствует структуре вашего документа XML; выполняя проход по этой структуре, вы можете извлечь информацию о документе и использовать ее для создания набора объектов С++.
Например, предположим, что у вас имеется XML-документ animals.xml , описывающий некоторое количество животных цирка, как показано в примере 14.1. Корень документа имеет имя animalListи содержит несколько дочерних элементов animal, каждый из которых представляет одно животное, принадлежащее цирку Feldman Family Circus. Предположим также, что у вас имеется класс C++ с именем Animal, и вам нужно сконструировать вектор std::vector, состоящий из объектов Animal, представляющих животных, перечисленных в документе.
Пример 14.1. Документ XML со списком животных цирка
Herby
elephant
1992-04-23
Sheldon
parrot
1998-09-30
Dippy
penguin
2001-06-08
Пример 14.2 показывает, как может выглядеть определение класса Animal. Animalимеет пять данных-членов, соответствующих кличке, виду, дате рождения, ветеринару и дрессировщику животного. Кличка и вид животного представляются строками типа std::string, дата его рождения представляется типом boost::gregorian::dateиз Boost.Date_Time, а его ветеринар и дрессировщик представляются экземплярами класса Contact, который определен также в примере 14.2. Пример 14.3 показывает, как можно использовать TinyXmlдля синтаксического анализа документа animals.xml , просмотра разобранного документа и заполнения вектора std::vectorобъектов Animal, используя извлеченные из документа данные.
Пример 14.2. Заголовочный файл animal.hpp
#ifndef ANIMALS_HPP_INCLUDED
#define ANIMALS_HPP_INCLUDED
#include
#include
#include // runtime_error
#include
#include
// Представляет ветеринара или дрессировщика
class Contact {
public:
Contact() {}
Contact(const std::string& name, const std::string& phone) :
name_(name) {
setPhone(phone);
}
std::string name() const { return name_; }
std::string phone() const { return phone_; }
void setName(const std::string& name) { name_ = name; }
void setPhone(const std::string& phone) {
using namespace std;
using namespace boost;
// Используйте Boost.Regex, чтобы убедиться, что телефон
// задач в форме (ddd)ddd-dddd
static regex pattern("\\([0-9]{3}\\)[0-9]{3}-[0-9]{4}");
if (!regex_match(phone, pattern)) {
throw runtime_error(string("bad phone number:") + phone);
}
phone_ = phone;
}
private:
std::string name_;
std::string phone_;
};
// Сравнить на равенство два объекта класса Contact; используется в рецепте
// 14.9 (для полноты следует также определить operator!=)
bool operator--(const Contact& lhs, const Contact& rhs) {
return lhs.name() == rhs.name() && lhs.phone() == rhs.phone();
}
// Записывает объект класса Contact в поток ostream
std::ostream& operator(std::ostream& out, const Contact& contact) {
Читать дальше