Интерфейсный класс 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() с очень похожим синтаксисом, которые формируют атрибут или элемент только для непустых значений.