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. |