`
Читать книги » Книги » Компьютеры и Интернет » Программирование » Д. Стефенс - C++. Сборник рецептов

Д. Стефенс - C++. Сборник рецептов

Перейти на страницу:

// на основе заданного набора атрибутов

Contact contactFromAttributes(const Attributes &attrs) {

 // Для повышения эффективности хранить часто используемые строки

 // в статических переменных

 static XercesString name = fromNative("name");

 static XercesString phone = fromNative("phone");

 Contact result;   // Возвращаемый объект Contact.

 const XMLCh* val; // Значение атрибута name или phone.

 // Установить имя объекта Contact.

 if ((val = attrs.getValue(name.c_str())) != 0) {

  result.setName(toNative(val));

 } else {

  throw runtime_error("contact missing name attribute");

 }

 // Установить номер телефона для объекта Contact.

 if ((val = attrs.getValue(phone.c_str())) != 0) {

  result.setPhone(toNative(val));

 } else {

  throw runtime_error("contact missing phone attribute");

 }

 return result;

}

// Реализует обратные вызовы, которые получают символьные данные и

// уведомления о начале и конце элементов

class CircusContentHandler : public DefaultHandler {

public:

 CircusContentHandler(vector<Animal>& animalList) :

  animalList_(animalList) {}

 // Если текущий элемент представляет ветеринара или дрессировщика

 // используйте attrs для конструирования объекта Contact для текущего

 // Animal; в противном случае очистите currentText_, подготавливая

 // обратный вызов characters()

 void startElement(

  const XMLCh *const uri,       // URI пространства имен

  const XMLCh *const localname, // имя тега без префикса NS

  const XMLCh *const qname,     // имя тега + префикс NS

  const Attributes &attrs)      // атрибуты элементов

 {

  static XercesString animalList = fromNative("animalList");

  static XercesString animal = fromNative("animal");

  static XercesString vet = fromNative("veterinarian");

  static XercesString trainer = fromNative("trainer");

  static XercesString xmlns =

   fromNative("http://www.feldman-family-circus.com");

  // проверить URI пространства имен

  if (uri != xmlns)

   throw runtime_error(

    string("wrong namespace uri ") + toNative(uri)

   );

  if (localname == animal) {

   // Добавить в список объект Animal; это будет

   // "текущий объект Animal"

   animalList_.push_back(Animal());

  } else if (localname != animalList) {

   Animal& animal = animalList_.back();

   if (localname == vet) {

    // Мы встретили элемент "ветеринар".

    animal.setVeterinarian(contactFromAttributes(attrs));

   } else if (localname == trainer) {

    // Мы встретили элемент "дрессировщик".

    animal.setTrainer(contactFromAttributes(attrs));

   } else {

    // Мы встретили элемент "кличка", "вид животного" или

    // "дата рождения". Их содержимое будет получено

    // при обратном вызове функции characters().

    currentText_.clear();

   }

  }

 }

 // Если текущий элемент представляет кличку, вид животного или дату

 // рождения, используйте хранимый в currentText_ текст для установки

 // соответствующего свойства текущего объекта Animal.

 void endElement(

  const XMLCh *const uri,       // URI пространства имен

  const XMLCh *const localname, // имя тега без префикса NS

  const XMLCh *const qname)     // имя тега + префикс NS

 {

  static XercesString animalList = fromNative("animal-list");

  static XercesString animal = fromNative("animal");

  static XercesString name = fromNative("name");

  static XercesString species = fromNative("species");

  static XercesString dob = fromNative("dateOfBirth");

  if (localname!= animal && localname!= animalList) {

   // currentText_ содержит текст элемента, который был

   // добавлен. Используйте его для установки свойств текущего

   // объекта Animal.

   Animal& animal = animalList_.back();

   if (localname == name) {

    animal setName(toNative(currentText_));

   } else if (localname == species) {

    animal.setSpecies(toNative(currentText_));

   } else if (localname == dob) {

    animal.setDateOfBirth(toNative(currentText_));

   }

  }

 }

 // Получает уведомления, когда встречаются символьные данные

 void characters(const XMLCh* const chars,

  const unsigned int length) {

  // Добавить символы в конец currentText_ для обработки методом

  // endElement()

  currentText_.append(chars, length);

 }

private:

 vector<Animal>& animalList_;

 XercesString currentText_;

};

Пример 14.7. SAX2 ErrorHandler

#include <stdexcept> // runtime_error

#include <xercesc/sax2/DefaultHandler.hpp>

// Получает уведомления об ошибках.

class CircusErrorHandler : public DefaultHandler {

public:

 void warning(const SAXParseException& e) {

  /* нет действий */

 }

 void error(const SAXParseExceptionf& e) {

  throw runtime_error(toNative(e.getMessage()));

 }

 void fatalError(const SAXParseException& e) { error(e); }

};

Пример 14.8. Синтаксический анализ документа animals.xml при помощи программного интерфейса SAX2

#include <exception>

#include <iostream> // cout

#include <memory>   // auto_ptr

#include <vector>

#include <xercesc/sax2/SAX2XMLReader.hpp>

#include <xercesc/sax2/XMLReaderFactory.hpp>

#include <xercesc/util/PlatformUtils.hpp>

#include "animal.hpp"

#include "xerces_strings.hpp" // Пример 14.4

using namespace std;

using namespace xercesc;

// Утилита RAII инициализирует парсер и освобождает ресурсы

// при выходе из области видимости

class XercesInitializer {

public:

 XercesInitializer() { XMLPlatformUtils::Initialize(); }

 ~XercesInitializer() { XMLPlatformUtils::Terminate(); }

private:

 // Запретить копирование и присваивание

 XercesInitializer(const XercesInitializer&);

 XercesInitializer& operator=(const XercesInitializer&);

};

int main() {

 try {

  vector<Animal> animalList;

  // Инициализировать Xerces и получить парсер

  XercesInitializer init;

  auto_ptr<SAX2XMLReader>

   parser(XMLReaderFactory::createXMLReader());

  // Зарегистрировать обработчики

  CircusContentHandler content(animalList);

  CircusErrorHandler error;

  parser->setContentHandler(&content);

  parser->setErrorHandler(&error);

  // Выполнить синтаксический анализ документа XML

  parser->parse("animals.xml");

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

  for (vector<Animal>::size_type i = 0;

   n = animalList.size(); i < n; ++i) {

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

  }

 } catch (const SAXException& e) {

  cout << "xml error: " << toNative(e.getMessage()) << "n";

  return EXIT_FAILURE;

 } catch (const XMLException& e) {

  cout << "xml error: " << toNative(e.getMessage()) << "n";

  return EXIT_FAILURE;

 } catch (const exception& e) {

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

  return EXIT_FAILURE;

 }

}

Обсуждение

Некоторые парсеры XML выполняют синтаксический анализ документа XML и возвращают его пользователю в виде сложного объекта С++. Именно это делает парсер TinyXml и парсер W3C DOM, который будет рассмотрен в следующем рецепте. В отличие от них парсер SAX2 использует ряд функций обратного вызова для передачи пользователю информации о документе XML по ходу его анализа. Функции обратного вызова сгруппированы в несколько интерфейсов обработчиков: ContentHandler получает уведомления об элементах, атрибутах и о тексте документа XML, ErrorHandler получает предупреждения и сообщения об ошибках, a DTDHandler получает уведомления о DTD документа XML.

Проектирование парсера, использующего функции обратного вызова, имеет несколько важных преимуществ. Например, можно выполнять синтаксический анализ очень больших документов, которые не помещаются в памяти. Кроме того, это может сэкономить процессорное время, потому что не надо выполнять многочисленные операции динамического выделения памяти, необходимые для конструирования узлов внутреннего представления документа XML, и потому что пользователь может создавать свое представление данных документа непосредственно, а не во время прохождения дерева документа, как я это делал в примере 14.3.

Пример 14.8 достаточно простой: я получаю парсер SAX2, регистрирую ContentHandler и ErrorHandler, анализирую документ animals.xml и печатаю список объектов Animal, заполненный обработчиком ContentHandler. Следует отметить два интересных момента: во-первых, функция XMLReaderFactory::createXMLReader() возвращает экземпляр SAX2XMLReader, память под который выделяется динамически и должна освобождаться пользователем в явной форме; для этой цели я использую std::auto_ptr, чтобы обеспечить удаление парсера даже в случае возникновения исключения. Во-вторых, среда Xerces должна быть инициализирована, используя xercesc::XMLPlatformUtils::Initialize(), и очищена при помощи xercesc::XMLPlatformUtils::Terminate(). Я инкапсулирую эту инициализацию и очистку в классе XercesInitializer, который вызывает XMLPlatformUtils::Initialize() в своем конструкторе и XMLPlatformUtils::Terminate() в своем деструкторе. Это гарантирует вызов Terminate(), даже если выбрасывается исключение. Это пример метода захвата ресурса при инициализации (Resource Acquisition Is Initialization — RAII), который был продемонстрирован в примере 8.3.

Перейти на страницу:

Откройте для себя мир чтения на siteknig.com - месте, где каждая книга оживает прямо в браузере. Здесь вас уже ждёт произведение Д. Стефенс - C++. Сборник рецептов, относящееся к жанру Программирование. Никаких регистраций, никаких преград - только вы и история, доступная в полном формате. Наш литературный портал создан для тех, кто любит комфорт: хотите читать с телефона - пожалуйста; предпочитаете ноутбук - идеально! Все книги открываются моментально и представлены полностью, без сокращений и скрытых страниц. Каталог жанров поможет вам быстро найти что-то по настроению: увлекательный роман, динамичное фэнтези, глубокую классику или лёгкое чтение перед сном. Мы ежедневно расширяем библиотеку, добавляя новые произведения, чтобы вам всегда было что открыть "на потом". Сегодня на siteknig.com доступно более 200000 книг - и каждая готова стать вашей новой любимой. Просто выбирайте, открывайте и наслаждайтесь чтением там, где вам удобно.

Комментарии (0)