XML - статьи

CSaxHandler – класс обработчиков SAX-анализатора


Пока мы будем использовать только три обработчика SAX-анализатора – обработчики начала и конца элементов, а также обработчик символьных данных. Это минимум, который необходим для разбора документа. Дополнительно могут потребоваться обработчики ошибок fatalError() и команд обработки processingInstruction(). Последний, в частности, может использоваться для определения кодировки документа, задаваемый в декларации XML атрибутом encoding.

Класс CSaxHandler порожден от класса Qt QxmlDefaultHandler, содержащего весь необходимый набор обработчиков парсера, которые по умолчанию ничего не делают. Для того чтобы расширить функциональность нашего класса, достаточно добавить в него объявление и реализацию соответствующих методов. Очень удобно.

// csaxhandler.h

#ifndef CSAXHANDLER_H #define CSAXHANDLER_H

#include <QXmlDefaultHandler> #include <QStack>

//---------------------------------------------------------------------- // обработчики для SAX-парсера //----------------------------------------------------------------------

class CNode;

class CSaxHandler : public QXmlDefaultHandler { private: CNode* doc; // указатель на объект QStack<CNode*> nodeStack; // стек обрабатываемых элементов QString textElement; // буфер содержимого текстового элемента QString encoding; // кодировка документа public: CSaxHandler(); CSaxHandler(CNode* node); virtual ~CSaxHandler();

// связывание объекта с обработчиками void setDocument(CNode* node); void reset(); // очистить стек и буферы

// обработчики bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &attributes); bool characters(const QString &str); bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName); }; //----------------------------------------------------------------------

#endif // CSAXHANDLER_H

Объект, с которым взаимодействует SAX-анализатор при разборе XML-документа, передается в обработчики в виде указателя doc. Это выполняется либо в конструкторе, либо в явном виде методом setDocument().

В определении класса (ниже) видно, что этот указатель помещается в стек nodeStack. В дальнейшем, по мере продвижения по содержимому документа, в этот стек помещаются и удаляются указатели на узлы объекта. Это обеспечивает работу с вложенными объектами узловых классов синхронно с разбором документа.


// csaxhandler.cpp



#include "csaxhandler.h" #include "cnode.h"

//----------------------------------------------------------------------

CSaxHandler::CSaxHandler(){ reset(); }

CSaxHandler::CSaxHandler(CNode* node){ setDocument(node); }

CSaxHandler::~CSaxHandler(){ // doc не удаляем (владелец - внешняя программа)! textElement.clear(); nodeStack.clear(); }

void CSaxHandler::reset(){ doc=0; textElement.clear(); nodeStack.clear(); }

void CSaxHandler::setDocument(CNode* node){ reset(); doc=node;

// корневой элемент nodeStack.push(doc); } //----------------------------------------------------------------------

bool CSaxHandler::startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &attributes){

if(nodeStack.isEmpty()) return false;

// текущий элемент CNode* node=nodeStack.top();

// обрабатываемый элемент if(node) node=node->getNode(localName);

// инициализация реквизитов if(node) node->setRequisites(localName,attributes);

// сделаем его текущим nodeStack.push(node); textElement.clear(); return true; } //----------------------------------------------------------------------

bool CSaxHandler::characters(const QString &str){ textElement+=str; return true; } //----------------------------------------------------------------------

bool CSaxHandler::endElement(const QString &namespaceURI, const QString &localName, const QString &qName){ if(nodeStack.isEmpty()) return false;

CNode* node=nodeStack.top();

// инициализация текстовых элементов if(node && node->isTextElement(localName)){ QXmlAttributes textAttr; textAttr.append(localName,"","",textElement); node->setRequisites(localName,textAttr); }

// элемент обработан nodeStack.pop(); return true; } //----------------------------------------------------------------------

Реквизиты объекта, соответствующие атрибутам исходного документа, инициализируются в обработчике startElement(), реквизиты, соответствующие символьным данным, – в endElement(). Для инициализации используется один и тот же метод интерфейсного класса setRequisites(). Для этого значение текстового элемента записывается в объект класса QXmlAttributes, используемого для передачи атрибутов.

Это искусственный прием, позволяющий сэкономить один метод в интерфейсе CNode. Правда, при этом немного усложняется реализация setRequisites() в узловых классах, поскольку в нем появляется дополнительный условный оператор. Альтернатива – добавление в интерфейс метода инициализации только текстовых реквизитов. Что лучше – судите сами. Автору представляется, что его вариант более экономный.

Собственно, этими двумя классами и ограничивается реализация общего подхода для разбора произвольных XML-документов. Как ими пользоваться – в следующем разделе на примере конкретного документа.


Содержание раздела