Работа с SAX


Ввиду большого расхода памяти, работа с моделью DOM не всегда желательна или возможна. Существует принципиально другой способ для анализа XML-документов — это SAX.

SAX (Simple API for XML, простой API для XML) является стандартом JavaAPI для считывания XML-документов. SAX применяется для последовательного считывания XML-данных, что позволяет без проблем работать с очень большими файлами.

Чтение XML-документа

Класс QXmlSimpleReader представляет собой XML-анализатор, базирующийся на SAX. Он читает XML-документ блоками и сообщает о том, что было найдено, с помощью соответствующих методов.

{рисунок}

В этом и состоит его основное преимущество: в память помещаются только фрагменты, а не весь XML-документ. Но это и недостаток, так как информация не считывается вся сразу и невозможно получить иерархию XML-документа. Класс QXmlContentHandler должен использоваться для соединения с объектом класса QXmlSimpleReader. Другие классы, такие как QXmlEntityResolver, QXmlDTDHandler, QXmlErrorHandler, QXmlDeclHandler и QXmlLexicalHandler, просто содержат определения виртуальных методов, соответствующих различным событиям анализа XML-документа.

В большинстве случаев, для считывания XML-документа можно прекрасно обойтись двумя классами: QXmlContentHandier и QXmlErrorHandler. Интерфейс класса QContentHandler содержит методы, связанные с отслеживанием структуры документа, вызов которых происходит в следующем порядке:

  1. метод startDocument() вызывается при начале чтения XML-документа;
  2. метод startElement() вызывается при начале чтения элемента;
  3. метод characters () вызывается при чтении данных элемента;
  4. метод endElement() вызывается при завершении обработки элемента;
  5. метод endDocument() вызывается при завершении обработки документа.

Для удобства существует класс QXmlDefaultHandler, который унаследован от всех шести классов. Он содержит пустые реализации виртуальных методов этих классов. Для чтения XML-документа нужно унаследовать его и перезаписать следующие методы: startDocument(), startElement(), characters(), endElement(), endDocument() и fatalError(). Определение последнего метода унаследовано от класса QXmlErrorHandler. Если эти методы возвращают значение true, то это говорит объекту класса QXmlSimpleReader о том, что он должен продолжить анализ файла. Значение false говорит об ошибке, а чтобы отобразить соответствующее сообщение о ней — необходимо перезаписать метод errorString().

Программа, приведенная в листингах, производит чтение XML-документа.


int main()
{
    AddressBookParser handler;
    QFile             file("addressbook.xml");
    QXmlInputSource   source(&file);
    QXmlSimpleReader  reader;

    reader.setContentHandler(&handler);
    reader.parse(source);

    return 0;
}

В основной программе создаются объекты классов AddressBookParser, QFile (для чтения файла) и QXmlSimpleReader (для анализа файла). Чтобы поместить XML-документ в SAX-анализатор, нужно создать объект класса QXmlInputSource, передав ему указатель на QIODevice. Созданный объект нужно передать в метод parse() объекта класса QXmlSimpleReader. Этот метод запустит процесс анализа XML-документа. До его вызова необходимо установить в методе setContenHandler() объект класса AddressBookparser, который производит отображение XML-документа.


class AddressBookParser : public QXmlDefaultHandler {
private:
    QString m_strText;

public:
    bool startElement(const QString&, 
                      const QString&, 
                      const QString&, 
                      const QXmlAttributes& attrs 
                     )
    {
        for(int i = 0; i < attrs.count(); i++) {
            if(attrs.localName(i) == "number") {
                qDebug() << "Attr:" << attrs.value(i);
            }
        }
        return true;
    }

    bool characters(const QString& strText) 
    {
        m_strText = strText;
        return true;
    }

    bool endElement(const QString&, const QString&, const QString& str)
    {    

        if (str != "contact" && str != "addressbook") {
            qDebug() << "TagName:" << str
                     << "\tText:"  << m_strText;
        }
        return true;
    }                                           

    bool fatalError(const QXmlParseException& exception)
    {
        qDebug() << "Line:"      << exception.lineNumber()
                 << ", Column:"  << exception.columnNumber()
                 << ", Message:" << exception.message();
        return false;
    }
};

Класс AddressBookParser реализует виртуальные методы, которые вызываются при прохождении по XML-документу. При нахождении тегов вызываются соответствующие методы startElement() и endElement(), которые должны быть перезаписаны для того, чтобы реагировать надлежащим образом. Метод startElement() вызывается тогда, когда при считывании встречается открытие тега. Третий параметр, передаваемый в этот метод, — это имя тега. Четвертый — это список атрибутов. В нашем случае, если название атрибута совпадает со строкой number, то производится вывод его значения. Метод characters() производит запись содержимого текущего элемента в переменную m_strText, а это необходимо для того, чтобы можно было получить доступ к этому содержимому из любого метода класса AddressBookParser. Метод endElement() вызывается всегда, когда при чтении встречается закрытие тега. Третий параметр, передаваемый в этот метод,— имя тега. В случае несовпадения его со строками contact и addressbook, производится отображение данных элемента. Метод fatalError() вызывается в том случае, если не удается проанализировать XML-документ. В этом случае метод производит отображение предупреждающего сообщения с указанием номера строки и столбца XML-документа, в котором произошел сбой и текст ошибки анализатора.

Qt предоставляет два способа работы с XML-документам и — DOM и SAX. Первый представляет данные XML-документа в виде иерархии (древовидной структуры), что очень удобно для работы. Второй способ считывает данные из XML-документа блоками и сообщает о результатах в определенные виртуальные методы.

При выборе следует учитывать, что SAX — это низкоуровневый способ и поэтому более быстрый. Его лучше всего применять в тех случаях, когда не требуется проведение очень сложных операций. Также следует применять его для считывания больших XML-документов, так как он более экономно расходует ресурсы памяти.

Читать далее: Динамические библиотеки и система расширений