XML - статьи

Интерфейсный класс CNode


Класс CNode является предком всех узловых классов объектного представления, включая корневой узел. Объявление этого класса следующее:

// cnode.h

#ifndef CNODE_H #define CNODE_H

#include <QString>

//---------------------------------------------------------------------- // CNode - узел объекта // Интерфейсный класс, обеспечивающий взаимодействие объекта и XML //----------------------------------------------------------------------

// Forward Decls class QXmlAttributes; class QXmlStreamWriter; class QIODevice;

class CNode { private: // вспомогательные методы работы с устройствами записи/чтения bool writeToDevice(QIODevice* device); bool readFromDevice(QIODevice* device); protected: // пространство имен и префикс элемента QString nodeNamespace; QString nodePrefix;

// методы для записи в XML необязательных реквизитов void writeAttribute(QXmlStreamWriter& writer,const QString& name, const QString& value); void writeTextElement(QXmlStreamWriter& writer,const QString& nsUri,const QString& name,const QString& text);

// интерфейсные методы - используются для чтения из XML SAX-парсером friend class CSaxHandler; virtual void setRequisites(const QString &name,const QXmlAttributes &attributes); virtual CNode* getNode(const QString &name); virtual bool isTextElement(const QString &name);

// интерфейсный метод - запись объекта в XML virtual bool writeNode(QXmlStreamWriter& writer,const QString& nsUri); public: CNode();

// наименование узла QString nodeName;

// чтение объекта из XML - из файла или символьного массива bool readDocument(const QString &fileName); bool readDocument(QByteArray* array);

// запись объекта в XML - в файл или символьный массив bool writeDocument(const QString &fileName); bool writeDocument(QByteArray* array);



// флаги, используемые при записи static QString encoding; // кодировка, используемая при записи static bool autoFormatting; // флаг форматирования XML при записи }; //----------------------------------------------------------------------


#endif // CNODE_H

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


  • void setRequisites(const QString &name,const QXmlAttributes &attributes) – инициализация реквизитов объекта;
  • CNode* getNode(const QString &name) – получение указателя на объект узлового класса; метод должен возвращать указатель на объект в случае успеха или 0, если объект с именем name не существует;
  • bool isTextElement(const QString &name) – метод индикации текстовых реквизитов, возвращает true, если реквизит с именем namе является текстовым, и false в противном случае;
  • bool writeNode(QXmlStreamWriter& writer,const QString& nsUri) – запись реквизитов узлового класса; реализация этого метода в прикладных классах зависит от того, какие средства используются для формирования XML-документа; ниже приведен пример реализации с использованием класса Qt QxmlStreamWriter.


Интерфейсный класс обеспечивает методами readDocument() и writeDocument() чтение и запись XML-документа в файл или символьный массив QByteArray, которые подключаются в качестве устройств ввода/вывода. Символьный массив играет роль строки, но с более широкими возможностями работы с различными кодировками XML-документов.

Обратите внимание на реквизит nodeName: его необходимо инициализировать в конструкторах прикладных классов именем элементов XML-документов, отображением которых эти классы являются.

Определение класса CNode также не отличается чрезмерной сложностью. Как уговаривались, для базового класса все интерфейсные методы имеют реализации по умолчанию, позволяющие не определять их в наследниках, если в этом нет необходимости:

// cnode.cpp

#include "cnode.h"

#include "cnode.h" #include "csaxhandler.h" #include <QFile> #include <QBuffer> #include <QXmlStreamWriter> //----------------------------------------------------------------------



QString CNode::encoding = "WINDOWS-1251"; bool CNode::autoFormatting = true; //----------------------------------------------------------------------

CNode::CNode(){ } //---------------------------------------------------------------------- // интерфейсные методы //----------------------------------------------------------------------

void CNode::setRequisites(const QString &name,const QXmlAttributes &attributes){ // ничего не делается - для классов, не содержащих реквизиты }

// указатель на узел элемент CNode* CNode::getNode(const QString &name){ if(name==nodeName) return this; else return 0; }

// проверка, является ли элемент текстовым bool CNode::isTextElement(const QString &name){ return false; }

bool CNode::writeNode(QXmlStreamWriter& writer,const QString& nsUri){ return true; } //---------------------------------------------------------------------- // запись необязательных реквизитов ЭС //----------------------------------------------------------------------

void CNode::writeAttribute(QXmlStreamWriter& writer,const QString& name, const QString& value){ if(!value.isEmpty()) writer.writeAttribute(name, value); }

void CNode::writeTextElement(QXmlStreamWriter& writer,const QString& nsUri,const QString& name,const QString& text){ if(!text.isEmpty()) writer.writeTextElement(nsUri,name,text); } //---------------------------------------------------------------------- // чтение из XML (при совпадении типов документа и объекта) //----------------------------------------------------------------------

bool CNode::readDocument(const QString &fileName){ QFile device(fileName); return readFromDevice(&device); }

bool CNode::readDocument(QByteArray* array){ QBuffer device(array); return readFromDevice(&device); }

bool CNode::readFromDevice(QIODevice* device){ if(!device->open(QIODevice::ReadOnly | QIODevice::Text)) return false;

QXmlInputSource xmlInputSource(device); CSaxHandler handler(this);



QXmlSimpleReader reader; reader.setContentHandler(&handler); bool ok=reader.parse(xmlInputSource);

device->close(); return true; } //---------------------------------------------------------------------- // запись в XML //----------------------------------------------------------------------

bool CNode::writeDocument(const QString &fileName){ QFile device(fileName); return writeToDevice(&device); }

bool CNode::writeDocument(QByteArray* array){ array->clear(); QBuffer device(array); return writeToDevice(&device); }

bool CNode::writeToDevice(QIODevice* device){ QXmlStreamWriter writer(device);

if(!device->open(QIODevice::WriteOnly)) return false;

writer.setAutoFormatting(autoFormatting);

// формирование xml-документа writer.setCodec(encoding.toAscii().data()); writer.writeStartDocument(); if(!nodeNamespace.isEmpty()) writer.writeNamespace(nodeNamespace, nodePrefix); // вызов виртуального метода writeNode(writer,nodeNamespace); writer.writeEndDocument();

device->close(); return true; } //----------------------------------------------------------------------

В качестве SAX-анализатора в приведенном коде используется класс Qt QXmlSimpleReader. Для его работы нужны обработчики, которые реализованы в виде класса CSaxHandler и помещены в отдельный модуль. Для записи документа используется, как уже упоминалось, класс Qt QXmlStreamWriter .

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

Во-первых, понятно, что метод чтения readDocument() вызывается для уже созданного объекта конкретного типа, и исходный XML-документ должен соответствовать этому типу. Поэтому в общем случае при чтении не известного заранее документа необходимо сначала определить его тип по имени корневого элемента и создать нужный объект. Это несложно, а то, как это сделать – смотрите в библиотеке QLibUfebs по приведенному выше адресу. Здесь же этот случай не рассматривается.

Что касается записи XML-документа, то в нашем случае для записи атрибутов и текстовых элементов в методах прикладного класса используются, соответственно, методы QXmlStreamWriter::writeAttribute() и QXmlStreamWriter::writeTextElement(). Чтобы облегчить реализацию записи необязательных реквизитов, предусмотрены методы CNode::writeAttribute() и CNode::writeTextElement() с очень похожим синтаксисом, которые формируют атрибут или элемент только для непустых значений.


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