| |
XML (от англ. Extensible Markup Language -- Расширяемый Язык Разметки) -- популярный формат файлов, используемый для обмена и хранения данных в текстовом виде.
Для работы с XML документами, Qt поддерживает два различных API:
SAX (от англ. Simple API for XML -- Простейший Прикладной Интерфейс для работы с XML) -- используется для выполнения синтаксического анализа методом обработки событий разбора прямо в приложении, с помощью виртуальных функций.
DOM (от англ. Document Object Model -- Объектная Модель представления Документов) -- преобразует XML-документ в древовидную структуру, в результате приложение получает возможность навигации по ней.
В этой главе мы покажем, как работать с XML-файлами посредством обоих API.
SAX -- это (де-факто) Java API стандарт для чтения XML-документов. Классы SAX, в библиотеке Qt, моделируют реализацию SAX2 Java, с небольшими отличиями в именованиях. Дополнительную информацию о SAX вы найдете по адресу: http://www.saxproject.org/.
Qt предоставляет SAX-парсер QXmlSimpleReader. Он распознает правильно оформленные XML-документы и поддерживает пространства имен XML. Во время анализа документа вызываются виртуальные функции классов-обработчиков событий разбора. (В данном случае, понятие "событие разбора" никак не пересекается с понятием событий в Qt.) Например, предположим, что парсер анализирует XML-документ со следующим содержимым:
<doc> <quote>Errare humanum est</quote> </doc>В этом случае парсер мог бы вызвать следующие обработчики событий разбора:
startDocument() startElement("doc") startElement("quote") characters("Errare humanum est") endElement("quote") endElement("doc") endDocument()Все вышеприведенные функции определены в классе QXmlContentHandler. С целью упрощения примера мы не приводим некоторые аргументы в функциях startElement() и endElement().
Класс QXmlContentHandler -- лишь один из многих, которые могут работать совместно с QXmlSimpleReader. Среди других классов можно назвать: QXmlEntityResolver, QXmlDTDHandler, QXmlErrorHandler, QXmlDeclHandler и QXmlLexicalHandler. Они реализуют исключительно виртуальные функции и предоставляют сведения о различного типа событиях разбора. В большинстве приложений используются только два класса: QXmlContentHandler и QXmlErrorHandler.
Для большего удобства, Qt так же предоставляет класс QXmlDefaultHandler, который наследует (через множественное наследование) и реализует все виртуальные функции других классов-обработчиков. Такая архитектура, со множеством абстрактных классов и единственным классом-наследником, довольно необычна для Qt, однако, она была принята в соответствии с моделью реализации, принятой в Java.
Рассмотрим на примере, как можно использовать классы QXmlSimpleReader и QXmlDefaultHandler для разбора XML-файла и отображения его содержимого в QListView. Наш класс, производный от класса QXmlDefaultHandler, будет называться SaxHandler. В его задачи будет входить разбор XML-документа, представляющего собой список терминов, использовавшихся в книге.
Рисунок 14.1. Дерево наследования класса SaxHandler.
<?xml version="1.0"?> <bookindex> <entry term="sidebearings"> <page>10</page> <page>34-35</page> <page>307-308</page> </entry> <entry term="subtraction"> <entry term="of pictures"> <page>115</page> <page>244</page> </entry> <entry term="of vectors"> <page>9</page> </entry> </entry> </bookindex>
Рисунок 14.2. Файл со списком терминов, использованных в книге, загруженный в QListView.
class SaxHandler : public QXmlDefaultHandler { public: SaxHandler(QListView *view); bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &attribs); bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName); bool characters(const QString &str); bool fatalError(const QXmlParseException &exception); private: QListView *listView; QListViewItem *currentItem; QString currentText; };Класс SaxHandler порожден от класса QXmlDefaultHandler и перекрывает четыре метода родителя: startElement(), endElement(), characters() и fatalError(). Первые три функции объявлены в классе QXmlContentHandler, последняя функция -- в QXmlErrorHandler.
SaxHandler::SaxHandler(QListView *view) { listView = view; currentItem = 0; }Конструктор получает указатель на QListView, который будет заполняться информацией из XML-файла.
bool SaxHandler::startElement(const QString &, const QString &, const QString &qName, const QXmlAttributes &attribs) { if (qName == "entry") { if (currentItem) { currentItem = new QListViewItem(currentItem); } else { currentItem = new QListViewItem(listView); } currentItem->setOpen(true); currentItem->setText(0, attribs.value("term")); } else if (qName == "page") { currentText = ""; } return true; }Функция startElement() вызывается, когда парсер встречает новый открывающий тег. Третий аргумент -- это имя тега. Четвертый -- список атрибутов. В данном примере мы будем игнорировать первый и второй аргументы. Они предназначены для работы с XML-файлами, которые используют механизм пространств имен.
Если это тег <entry>, создается новый элемент списка QListView. Если анализируемый тег вложен в другой тег <entry>, создается вложенный подэлемент списка -- QListViewItem. В противном случае создается элемент списка верхнего уровня. Функция setOpen(true) вызывается для того, чтобы открыть вложенные подэлементы данного элемента. Функция setText() записывает текст (значение атрибута term), который будет отображаться на экране в первой колонке списка.
Если это тег <page>, то в currentText записывается пустая строка. Переменная currentText служит своего рода аккумулятором для текста, размещаемого между тегами <page> и </page>.
В заключение, в вызывающую программу возвращается true, чтобы сообщить парсеру SAX о том, что он может продолжить разбор файла. В случае неопознанного тега, можно вернуть false, чтобы известить парсер об ошибке. В этом случае необходимо тогда перекрыть метод errorString(), унаследованный от QXmlDefaultHandler, чтобы вернуть соответствующее сообщение об ошибке.
bool SaxHandler::characters(const QString &str) { currentText += str; return true; }Функция characters() вызывается для передачи символьных данных из XML-файла. В нашем случае мы просто добавляем их в конец переменной currentText.
bool SaxHandler::endElement(const QString &, const QString &, const QString &qName) { if (qName == "entry") { currentItem = currentItem->parent(); } else if (qName == "page") { if (currentItem) { QString allPages = currentItem->text(1); if (!allPages.isEmpty()) allPages += ", "; allPages += currentText; currentItem->setText(1, allPages); } } return true; }Функция endElement() вызывается, когда парсер встречает закрывающий тег. Аналогично функции startElement(), третьим аргументом ей передается имя тега.
Если это тег </entry>, то текущим назначается элемент более высокого уровня. Таким образом восстанавливается значение переменной, которое предшествовало открывающему тегу <entry>.
Если это тег </page>, производится добавление номеров в список страниц, которые отображаются во второй колонке списка.
bool SaxHandler::fatalError(const QXmlParseException &exception) { qWarning("Line %d, column %d: %s", exception.lineNumber(), exception.columnNumber(), exception.message().ascii()); return false; }Функция fatalError() вызывается, когда парсер не может продолжить разбор XML-файла. Тогда мы просто выводим сообщение, с указанием номера строки и позиции в строке, где была обнаружена ошибка.
На этом мы завершаем обзор реализации класса SaxHandler и переходим к демонстрации практического его применения:
bool parseFile(const QString &fileName) { QListView *listView = new QListView(0); listView->setCaption(QObject::tr("SAX Handler")); listView->setRootIsDecorated(true); listView->setResizeMode(QListView::AllColumns); listView->addColumn(QObject::tr("Terms")); listView->addColumn(QObject::tr("Pages")); listView->show(); QFile file(fileName); QXmlSimpleReader reader; SaxHandler handler(listView); reader.setContentHandler(&handler); reader.setErrorHandler(&handler); return reader.parse(&file); }Сначала создается виджет QListView с двумя колонками. Затем создаются объект QFile, посредством которого будет выполняться работа с файлом XML-документа, и QXmlSimpleReader -- сам парсер. У нас нет необходимости открывать файл -- за нас это сделает сама библиотека Qt.
В заключение создается объект SaxHandler. Мы передаем его парсеру, как обработчик событий разбора и как обработчик ошибок. И наконец запускаем процесс разбора, вызовом parse().
Пред. | В начало | След. |
Протокол UDP и класс QSocketDevice. | На уровень выше | Чтение XML-документов с помощью DOM. |
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |