Парсер SAX2 вызывает метод endElement()
startElement()
. В реализации endElement()
, приведенной в примере 14.6, я игнорирую все элементы, отличные от name
, species
и dateOfBirth
. Когда происходит обратный вызов, соответствующий одному из этих элементов, сигнализирующий о сделанном только что выходе парсера из элемента, я использую символьные данные, сохраненные в currentText_
для установки клички, вида и даты рождения текущего объекта Animal
.Несколько важных особенностей SAX2 не проиллюстрировано в примерах 14.6, 14.7 и 14.8. Например, класс SAX2XMLReader
parse()
, которая принимает в качестве аргумента экземпляр xercesc::InputSource
вместо строки в С-стиле. InputSource
является абстрактным классом, инкапсулирующим источник символьных данных; конкретные его подклассы, в том числе xercesc::MemBufInputSource
и xercesc::URLInputSource
, позволяют парсеру SAX2 анализировать документ XML, который находится не в локальной файловой системе.Более того, интерфейс ContentHandler
startDocument()
и endDocument()
, которые сигнализируют о начале и конце документа XML, и setLocator()
, который позволяет задать объект Locator
, отслеживающий текущую позицию анализируемого файла. Существуют также другие интерфейсы обработчиков, включая DTDHandler
и EntityResolver
(соответствующие базовой спецификации SAX 2.0), а также DeclarationHandler
и LexicalHandler
(соответствующие стандартизованным расширениям SAX 2.0).Кроме того, можно в одном классе реализовать несколько интерфейсов обработчиков. Это можно легко сделать в классе xercesc::DefaultHandler
CircusErrorHandler
в CircusContentHandler
и следующим образом модифицировать пример 14.8.// Зарегистрировать обработчики
CircusContentHandler handler(animalList);
parser->setContentHandler(&handler);
parser->setErrorHandler(&handler);
Пример 14.8 имеет еще одну, последнюю особенность, которую вы должны были заметить: обработчик CircusContentHandler
animalList
или что все дочерние элементы корня являются элементами animal
. Это сильно отличается от примера 14.3. Например, функция main()
из примера 14.3 проверяет то, что элементом верхнего уровня является animalList
, а функция nodeToAnimal()
проверяет то, что ее аргументы представляют элемент animal
, содержащий точно пять дочерних элементов типа name
, species
, dateOfBirth
, veterinarian
и trainer
.Пример 14.6 можно модифицировать, чтобы он выполнял подобного рода проверки. Например, обработчик ContentHandler
animalList
и что его дочерние элементы имеют тип animal
, а дочерние элементы элемента animal
не содержат других элементов. Это можно сделать с помощью трех флагов типа boolean
, parsingAnimalList_
, parsingAnimal_
и parsingAnimalChild_
, которые регистрируют анализируемую в данный момент область документа. Методы startElement()
и endElement()
просто обновляют эти флаги и проверяют их согласованность, делегируя задачу обновления текущего объекта Animal вспомогательным методам startAnimalChild()
и endElementChild()
, реализация которых очень напоминает реализацию методов startElement()
и endElement()
из примера 14.6.// Реализует функции обратного вызова, которые получают символьные данные и
// уведомляют о начале и конце элементов
class CircusContentHandler : public DefaultHandler {
public:
CircusContentHandler(vector