Выбрать главу

   animal; animal = animal->NextSiblingElement()) {

   animalList.push_back(nodeToAnimal(animal));

  }

  // Напечатать клички животных

  for (vector<Animal>::size_type i = 0, n = animalList.size(); i < n; ++i) {

   cout << animalList[i] << "\n";

  }

 } catch (const exception& e) {

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

  return EXIT_FAILURE;

 }

}

Обсуждение

TinyXml (буквально «крошечный XML») очень хорошо подходит в тех случаях, когда требуется выполнять несложную обработку документов XML. Дистрибутив исходных текстов этой библиотеки небольшой, ее легко построить и интегрировать в проекты, и она имеет очень простой интерфейс. Она также имеет очень либеральную лицензию. Главными ограничениями TinyXml являются невосприимчивость к пространствам имен XML, невозможность контроля DTD или схемы, а также невозможность анализа документов XML с внутренним DTD. Если вам требуется какая-то из этих функций или какая-нибудь XML-технология, как, например, XPath или XSLT, то необходимо воспользоваться другими библиотеками, рассмотренными в данной главе.

На выходе парсера TinyXml получается документ XML в виде дерева, узлы которого представляют элементы, текст, комментарии и другие компоненты документа XML. Корень дерева представляет собственно документ XML. Такое иерархическое представление документа называется объектной моделью документа (Document Object Model - DOM). Модель DOM, полученная парсером TinyXml, аналогична модели, разработанной консорциумом W3C (World Wide Web Consortium), хотя она и не полностью соответствует спецификации W3C. Вследствие приверженности библиотеки TinyXml принципам минимализма модель TinyXml DOM проще W3С DOM, однако она обладает меньшими возможностями.

Получить доступ к узлам дерева, представляющего документ XML, можно с помощью интерфейса TiXmlNode, который содержит методы, обеспечивающие доступ к родительскому узлу, последовательный доступ ко всем дочерним узлам, удаление и добавление дочерних узлов. Каждый узел является экземпляром некоторого производного типа; например, корень дерева является экземпляром TiXmlDocument, узлы элементов являются экземплярами TiXmlElement, а узлы, представляющие текст, являются экземплярами TiXmlText. Тип TiXmlNode можно определить с помощью его метода Туре(); зная тип узла, вы можете получить конкретное его представление с помощью таких методов, как toDocument(), toElement() и toText(). Эти производные типы содержат дополнительные методы, характерные для узлов конкретного типа.

Теперь несложно разобраться с примером 14.3. Во-первых, функция textValue() извлекает текстовое содержимое из элементов, содержащих только текст, например name, species или dateOfBirth. В этом случае данная функция сначала убеждается, что имеется только один дочерний элемент и что он является текстовым узлом. Она затем получает текст дочернего элемента, вызывая метод Value(), который возвращает текстовое содержимое текстового узла или узла комментария, имя тега узла элемента и имя файла корневого узла.

На следующем шаге функция nodeToContact() получает узел, соответствующий элементу veterinarian или trainer, и конструирует объект Contact из значений атрибутов name и phone, получаемых с помощью метода Attribute().

Аналогично функция nodeToAnimal() получает узел, соответствующий элементу животного element, и конструирует объект Animal. Это делается путем прохода по дочерним узлам с помощью метода NextSiblingElement(), извлекая при этом содержащиеся в каждом элементе данные и устанавливая соответствующее свойство объекта Animal. Данные извлекаются, используя функцию textValue() для элементов name, species и dateOfBirth и функцию nodeToContact() для элементов veterinarian и trainer.

В функции main я сначала конструирую объект TiXmlDocument соответствующий файлу animals.xml, и выполняю его синтаксический разбор с помощью метода LoadFile(). Затем я получаю элемент TiXmlElement, соответствующий корню документа, вызывая метод RootElement(). На следующем шаге я просматриваю все дочерние узлы корневого элемента, конструируя объект Animal из каждого элемента animal с помощью функции nodeToAnimal(). Наконец, я прохожу по всем объектам Animal, записывая их в стандартный вывод.

В примере 14.3 не проиллюстрирована одна функция библиотеки TinyXml, а именно метод SaveFile() класса TiXmlDocument, который записывает в файл документ, представляемый объектом TiXmlDocument. Это позволяет выполнить синтаксический разбор документа XML, модифицировать его, используя интерфейс DOM, и сохранить модифицированный документ. Документ TiXmlDocument можно создать даже с чистого листа и затем сохранить его на диске.

// Создать документ hello.xml, состоящий

// из единственного элемента "hello"

TiXmlDocument doc;

TiXmlElement root("hello");

doc.InsertEndChild(root);

doc.SaveFile("hello.xml");

Смотри также

Рецепты 14.3 и 14.4.

14.2. Работа со строками Xerces

Проблема

Требуется обеспечить надежную и простую работу со строками с расширенным набором символов, используемыми библиотекой Xerces. В частности, необходимо уметь сохранять строки, возвращаемые функциями библиотеки Xerces, а также выполнять преобразования между строками Xerces и строками стандартной библиотеки С++.

Решение

Сохранять строки с расширенным набором символов, возвращаемые функциями библиотеки Xerces, можно с помощью шаблона std::basic_string, специализированного типом с расширенным набором символов XMLCh библиотеки Xerces.

typedef std::basic_string<XMLCh> XercesString;

Для выполнения преобразований между строками Xerces и строками, состоящими из стандартных символов, используйте перегруженный статический метод transcode() из класса xercesc::XMLString, который определен в заголовочном файле xercesc/util/XMLString.hpp.

В примере 14.4 определяются две перегруженные вспомогательные функции, toNative и fromNative, которые используют transcode для преобразования строк со стандартными символами в строки Xerces и обратно. Каждая функция имеет две версии: одна принимает строку в C-стиле, а другая принимает строку стандартной библиотеки С++. Для выполнения преобразований между строками Xerces и строками со стандартными символами вполне достаточно иметь эти служебные функции; после того как вы определили эти функции, вам уже никогда не потребуется вызывать непосредственно transcode.

Пример 14.4. Заголовочный файл xerces_strings.hpp, используемый для выполнения преобразований между строками Xerces и строками со стандартными символами

#ifndef XERCES_STRINGS_HPP_INCLUDED

#define XERCES_STRINGS_HPP_INCLUDED

#include <string>

#include <boost/scoped_array.hpp>

#include <xercesc/util/XMLString.hpp>