Пример 14.1. Документ XML со списком животных цирка
<?xml version="1.0" encoding="UTF-8"?>
<!- Животные цирка Feldman Family Circus -->
<animalList>
 <animal>
  <name>Herby</name>
  <species>elephant</species>
  <dateOfBirth>1992-04-23</dateOfBirth>
  <veterinarian name="Dr. Hal Brown" phone="(801)595-9627"/>
  <trainer name="Bob Fisk" phone=(801)881-2260"/>
 </animal>
 <animal>
  <name>Sheldon</name>
  <species>parrot</species>
  <dateOfBirth>1998-09-30</dateOfBirth>
  <veterinarian name="Dr Kevin Wilson" phone="(801)466-6498"/>
  <trainer name="Eli Wendel" phone="(801)929-2506"/>
 </animal>
 <animal>
  <name>Dippy</name>
  <species>penguin</species>
  <dateOfBirth>2001-06-08</dateOfBirth>
  <veterinarian name= "Dr. Barbara Swayne" phone="(801)459-7746"/>
  <trainer name="Ben Waxman" phone="(801)882-3549"/>
 </animal>
</animalList>
Пример 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 <ostream>
#include <string>
#include <stdexcept> // runtime_error
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/regex.hpp>
// Представляет ветеринара или дрессировщика
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) {
 out << contact.name() << " " << contact.phone(); return out;
}
// Класс Animal представляет животное
class Animal {
public:
 // Конструктор по умолчанию класса Animal; этот конструктор будет вами
 // использоваться чаще всего Animal() {}
 // Конструирование объекта Animal с указанием свойств животного;
 // этот конструктор будет использован в рецепте 14.9
 Animal(const std::string& name,
  const std::string& species, const std::string& dob,
  const Contact& vet, const Contact& trainer) :
  name_(name), species_(species), vet_(vet), trainer_(trainer) {
   setDateOfBirth(dob)
  }
 // Функции доступа к свойствам животного
 std::string name() const { return name_; }
 std::string species() const { return species_; }
 boost::gregorian::date dateOfBirth() const { return dob_; )
 Contact veterinarian() const { return vet_; }
 Contact trainer() const { return trainer_; }
 // Функции задания свойств животного
 void setName(const std::string& name) { name_ = name; }
 void setSpecies(const std::string& species) { species_ = species; }
 void setDateOfBirth(const std::string& dob) {
  dob_ = boost::gregorian::from_string(dob);
 }
 void setVeterinarian(const Contact& vet) { vet_ = vet; }
 void setTrainer(const Contact& trainer) { trainer_ = trainer; }
private:
 std::string name_;
 std::string species_;
 boost::gregorian::date dob_;
 Contact vet_;
 Contact trainer_;
};
// Сравнение на равенство двух объектов Animal; используется в рецепте 14.9
// (для полноты следует также определить operator!=)
bool operator==(const Animal& lhs, const Animal& rhs) {
 return lhs.name() == rhs.name() && lhs.species() == rhs.species() &&
  lhs.dateOfBirth() == rhs.dateOfBirth() &&
  lhs.veterinarian() == rhs.veterinarian() &&
  lhs.trainer() == rhs.trainer();
}
// Записывает объект Animal в поток ostream
std::ostream& operator<<(std::ostream& out, const Animal& animal) {
 out << "Animal {n"
  << " name=" << animal.name() << ";n"
  << " species=" << animal.species() << ";n"
  << date-of-birth=" << animal.dateOfBirth() << ";n"
  << " veterinarian=" << animal.veterinarian() << ";n"
  << " trainer=" << animal.trainer() << ";n"
  << "}";
 return out;
}
#endif // #ifndef ANIMALS_HPP_INCLUDED
Пример 14.3. Синтаксический анализ animals.xml с помощью TinyXml
#include <exception>
#include <iostream>  // cout
#include <stdexcept> // runtime_error
#include <cstdlib>   // EXIT_FAILURE
#include <cstring>   // strcmp
#include <vector>
#include <tinyxml.h>
#include "animal.hpp"
using namespace std;
// Извлекает текстовое содержимое элемента XML
const char* textValue("TiXmlElement* e) {
 TiXmlNode* first = fi->FirstChild();
 if (first != 0 && first == e->LastChild() &&
  first->Type() == TiXmlNode::TEXT) {
  // элемент «е» имеет один дочерний элемент типа TEXT;
  // возвратить дочерний элемент
  return first->Value();
 } else {
  throw runtime_error(string("bad ") + e->Value() + " element");
 }
}
// Конструирует объект класса Contact из элементов ветеринара или
// дрессировщика ("veterinarian" или "trainer")
Contact nodeToContact(TiXmlElement* contact) {
 using namespace std;
 const char *name, *phone;
 if (contact->FirstChild() == 0 &&
  (name = contact->Attribute("name")) &&
  (phone = contact->Attribute("phone"))) {
  // Элемент contact не имеет дочерних элементов и имеет атрибуты имени
  // и телефона ("name" и "phone"); используйте эти значения для
  // конструирования объекта Contact
  return Contact(name, phone);
 } else {
  throw runtime_error(string("bad ") + contact->Value() + " element");
 }
}
// Конструирует объект Animal из элемента животного ("animal")
Animal nodeToAnimal(TiXmlElement* animal) {
 using namespace std;
 // Убедиться, что animal соответствует элементу "animal"
 if (strcmp(animal->Value(), "animal") != 0) {
  throw runtime_error(string("bad animal: ") + animal->Value());
 }
 Animal result; // Возвратить значение
 TiXmlElement* element = animal->FirstChildElement();
 // Прочитать элемент клички животного
 if (element && strcmp(element->Value(), "name") == 0) {
  // Первым дочерним элементом объекта animal является кличка (элемент
  // name"); используйте ее текстовое значение для установки клички
  // в объекте result
  result.setName(textValue(element));
 } else {
  throw runtime_error("no name attribute");
 }
 // Прочитать элемент вида животного
 element = element->NextSiblingElement();
 if (element && strcmp(element->Value(), species") == 0) {
  // Вторым дочерним элементом animal является вид животного
  // (элемент "species"); используйте его текстовое значение для
  // установки вида в объекте result
  result.setSpecies(textValue(element));
 } else {
  throw runtime_error(""no species attribute");
 }
 // Прочитать элемент даты рождения
 element = element->NextSiblingElement();
 if (element && strcmp(element->Value(), "dateOfBirth") == 0) {
  // Третьим дочерним элементом animal является дата рождения
  // (элемент "dateOfBirth"));
  // используйте его текстовое значение для установки даты
  // рождения в объекте result
  result.setDateOfBirth(textValue(element));
 } else {
  throw runtime_error("no dateOfBirth attribute");