Несколько важных особенностей 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в примере 14.9 удостоверяется в том, что корневым элементом документа является animalListи что его дочерние элементы имеют тип animal, а дочерние элементы элемента animalне содержат других элементов. Это можно сделать с помощью трех флагов типа boolean, parsingAnimalList_, parsingAnimal_и parsingAnimalChild_, которые регистрируют анализируемую в данный момент область документа. Методы startElement()и endElement()просто обновляют эти флаги и проверяют их согласованность, делегируя задачу обновления текущего объекта Animal вспомогательным методам startAnimalChild()и endElementChild(), реализация которых очень напоминает реализацию методов startElement()и endElement()из примера 14.6.
Пример 14.9. Обработчик SAX2 ContentHandler документа animals.xml, который проверяет структуру документа
// Реализует функции обратного вызова, которые получают символьные данные и
// уведомляют о начале и конце элементов
class CircusContentHandler : public DefaultHandler {
public:
CircusContentHandler(vector& animalList)
: animalList_(animalList), // заполняемый список
parsingAnimalList_(false), // состояние анализа
parsingAnimal_(false), // состояние анализа
parsingAnimalChild_(false) // состояние анализа
{}
// Получает уведомления от парсера при каждой встрече начала
// какого-нибудь элемента
void startElement(
const XMLCh *const uri, // uri пространства имен
const XMLCh *const localname, // простое имя тега
const XMLCh *const qname, // квалифицированное имя тега
const Attributes &attrs) // Набор атрибутов
{
static XercesString animalList = fromNative("animalList");
static XercesString animal = fromNative("animal");
static XercesString xmlns =
fromNative("http://www.feldman-family-circus.com");
// Проверяет uri пространства имен
if (uri != xmlns)
throw runtime_error(
string("wrong namespace uri: ") + toNative(uri)
);
// (i) Обновить флаги parsingAnimalList_, parsingAnimal_
// и parsingAnimalChild_, которые показывают, в какой части
// документа мы находимся
// (ii) Убедиться, что элементы имеют правильную вложенность
Читать дальше