Некоторые парсеры 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.