выкладываю:
H файл: XmlSettings.h
C++ (Qt)
#ifndef __XML_SETTINGS_H__
#define __XML_SETTINGS_H__
#include <QString>
#include <QSettings>
class QDomDocument;
class QDomElement;
class QIODevice;
class CXmlSettings
{
QSettings * m_pSettings;
public:
CXmlSettings( QString fname = "" );
~CXmlSettings();
inline QSettings& settings() const { return * m_pSettings; }
// при использование CXmlSettings::value конфиг создастся автоматически, если его не было
QVariant value( const QString & key, const QVariant & defaultValue = QVariant() );
void setValue ( const QString & key, const QVariant & value );
private:
static bool readXmlFile(QIODevice &device, QSettings::SettingsMap &map);
static bool writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map);
static void processWriteKey( QDomDocument& doc, QDomElement& domElement, QString key, const QVariant& value );
static void processReadKey( QString key, QSettings::SettingsMap &map, QDomElement& domElement );
};
#endif // __XML_SETTINGS_H__
CPP файл: XmlSettings.cpp
C++ (Qt)
#include "XmlSettings.h"
#include "FileUtils.h"
#include <QDomDocument>
#include <QDebug>
CXmlSettings::CXmlSettings( QString fname )
{
if ( fname.isEmpty() )
fname = settingsPath() + appBaseName() + ".xml";
static const QSettings::Format XmlFormat = QSettings::registerFormat("xml", readXmlFile, writeXmlFile);
m_pSettings = new QSettings( fname, XmlFormat );
}
CXmlSettings::~CXmlSettings()
{
delete m_pSettings;
}
QVariant CXmlSettings::value( const QString & key, const QVariant & defaultValue )
{
if ( !settings().contains( key ) )
settings().setValue( key, defaultValue );
return settings().value( key );
}
void CXmlSettings::setValue ( const QString & key, const QVariant & value )
{
settings().setValue( key, value );
}
bool CXmlSettings::readXmlFile(QIODevice &device, QSettings::SettingsMap &map)
{
qDebug() << "-----readXmlSettings------";
QDomDocument doc("");
if ( !doc.setContent( &device ) ) return false;
QDomElement root = doc.documentElement();
processReadKey( "", map, root );
/*
// Для теста использовалось при отладке
QMap<QString, QVariant>::const_iterator i = map.constBegin();
while (i != map.constEnd()) {
qDebug() << i.key() << ": " << i.value() << endl;
++i;
}
*/
return true;
}
bool CXmlSettings::writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map)
{
qDebug() << "-----writeXmlSettings-----";
/*
// Для теста использовалось при отладке
QMap<QString, QVariant>::const_iterator i = map.constBegin();
while (i != map.constEnd()) {
qDebug() << i.key() << ": " << i.value() << endl;
++i;
}
*/
QDomDocument doc("");
QDomElement root = doc.createElement("Main");
doc.appendChild(root);
QMapIterator<QString, QVariant> i(map);
while ( i.hasNext() )
{
i.next();
QString sKey = i.key();
QVariant value = i.value();
processWriteKey( doc, root, sKey, i.value() );
};
QDomNode xmlNode = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
doc.insertBefore(xmlNode, doc.firstChild());
QTextStream out( &device );
doc.save(out, 4);
return true;
}
void CXmlSettings::processWriteKey( QDomDocument& doc, QDomElement& domElement, QString key, const QVariant& value )
{
int slashPos = key.indexOf( '/' );
// переданный ключ является параметром
if ( slashPos < 0 )
{
// не пишем в конфиг параметр size (является ограничением - нельзя исп. пар-тр с таким именем)
if ( key == "size" ) return;
domElement.setAttribute( key, value.toString() );
return;
};
// получение имени группы соответствующей xml ноде
QString groupName = key.left( slashPos );
// если в качестве имени использован числовой параметр - это табличная строка, преобразуем ее в row_?
if ( groupName.toInt() )
{
groupName = "row_" + groupName;
domElement.toElement().setAttribute("table", "1");
};
// поиск/создание ноды соответствующей ключу
QDomElement groupElement;
QDomNode findedGroupNode = domElement.namedItem( groupName );
if ( findedGroupNode.isNull() )
{
groupElement = doc.createElement( groupName );
domElement.appendChild( groupElement );
}
else
groupElement = findedGroupNode.toElement();
// готовим обрезанную часть ключа
key = key.right( key.size() - slashPos - 1 );
// продолжение обработки (создание/поиск групп) - пока не найдется параметр
processWriteKey( doc, groupElement, key, value );
}
void CXmlSettings::processReadKey( QString key, QSettings::SettingsMap &map, QDomElement& domElement )
{
QDomNamedNodeMap namedNodeMap = domElement.attributes();
// Добавление всех атрибутов элемента в качестве значений
for (int i = 0; i < namedNodeMap.count(); ++i)
{
QString name = namedNodeMap.item( i ).toAttr().name();
QString value = namedNodeMap.item( i ).toAttr().value();
map.insert( key + name, value );
};
QDomNodeList nlChild = domElement.childNodes();
// если узел является таблицей - то все дети строки
bool isTable = domElement.attribute("table", "0").toInt();
// создаем доп. элемент size равный числу детей (необходим для QSettings - beginArray)
if ( isTable )
map.insert( key + "size", nlChild.count() );
// проход по всем детям
for (int i = 0; i < nlChild.count(); ++i)
{
QString childName = nlChild.item(i).toElement().tagName();
if ( childName.contains("row_") )
childName = childName.right( childName.size() - 4 );
QString subKey = key + childName + "/";
QDomElement subElement = nlChild.item(i).toElement();
processReadKey( subKey, map, subElement );
};
}
Функции settingsPath() и appBaseName() - у меня реализованы в моем FileUtils - возвращают дефолтный путь к папке с настройками и базовое имя программы (без расширения)
Также продублировал во вложении.
Пользоваться можно через обращение к settings() - всеми возможностями самого QSettings - группы, массивы и т.д. Ограничение - нельзя использовать атрибут с именем "table" - он используется для определения таблицы (массива) при чтение настроек. Если пользоваться через прямые методы value, CXmlSettings - то в случае если значения в конфиге не было - оно создасться и заполниться значением по умолчанию (сделал чтобы программа сама создавала свой конфиг если был утерян...).