Zacznę od metody pomocniczej: FillObjectPropertiesBasedOnXMLNodeList
void QXmlSerialization::FillObjectPropertiesBasedOnXMLNodeList(const QDomNodeList &list, QObject* object)
{
const QMetaObject* metaObject = object->metaObject();
for(int childIndex = 0; childIndex < list.count(); childIndex++)
{
QDomNode rootChildNode = list.at(childIndex);
QDomElement rootChildElement = rootChildNode.toElement();
QMetaProperty rootProperty = metaObject->property(metaObject->indexOfProperty(rootChildNode.nodeName().toUtf8()));
QString rootPropertyName = rootProperty.typeName();
QString content = rootChildElement.text();
QVariant::Type type = rootProperty.type();
QVariant result;
switch (type)
{
case QVariant::UserType:
{
//create instance of an object based on it's registered type
int listElementType = QMetaType::type(rootPropertyName.toUtf8());
#if defined(Q_OS_WIN)
QObject* innerObject = QMetaType::metaObjectForType(rootProperty.type())->newInstance();
#else
QObject* innerObject = QMetaType::metaObjectForType(listElementType)->newInstance();
#endif
FillObjectPropertiesBasedOnXMLNodeList(rootChildNode.childNodes(), innerObject);
result.setValue(innerObject);
}
break;
case QVariant::List: //to jest ta mała zmiana
case QVariant::Hash:
case QVariant::Map:
qDebug("Unhandled type");
break;
case QVariant::String:
default:
{
result.setValue(content);
}
break;
}
rootProperty.write(object, result);
}
}
Ta oto metoda tak naprawdę przypisuje odnalezione wartości tagów XML do odpowiadających im nazwą właściwościom podanego w parametrze obiektu. Nic bardziej oczywistego. Z początku próbowałem zachować strukturę podobną do tej z poprzedniego wpisu, ale ostatecznie okazało się, że wielokrotnie natrafiałem na punkt bez wyjścia.
Ta metoda pozostawia jeszcze wiele do życzenia (szczególnie znienawidzony przeze mnie #if defined(Q_OS_WIN)). W formie ostatecznej [https://github.com/Dorrro/QXmlSerialization] uzupełniłem ją jeszcze o obsługę List (tak naprawdę QVarianList). Wiele do życzenia pozostawia też sam Qt, który inaczej w tej samej wersji zachowuje się na Windowsie i inaczej na Linuxie (multiplatformowość FTW!). Nie czas jednak narzekać na Qt – to innym razem.
Czas natomiast na odpowiedzialną za wszelkie zło metodę: Deserialize
void QXmlSerialization::Deserialize(const QDomDocument &doc, QObject *targetObject)
{
const QMetaObject *metaobject = targetObject->metaObject();
QDomNodeList rootChildNodes = doc.documentElement().childNodes();
FillObjectPropertiesBasedOnXMLNodeList(rootChildNodes, targetObject);
}
Ot, to wszystko.
Czas na: How to use it? Ulepszyłem nieco poprzedni TestObject oraz dodałem drugi, tak, żeby można było zobaczyć jak w pełni swoich możliwości działa deserializacja
#ifndef TESTOBJECT
#define TESTOBJECT
#include
#include
class TestObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString TestString READ TestString WRITE setTestString NOTIFY TestStringChanged)
Q_PROPERTY(TestObject2* test READ test WRITE settest NOTIFY testChanged)
public:
Q_INVOKABLE explicit TestObject(QObject *parent = 0) : QObject(parent) {}
//properties
void setTestString(const QString &string)
{
if (string != m_TestString) {
m_TestString = string;
emit TestStringChanged();
}
}
QString TestString() const {
return m_TestString;
}
void settest(TestObject2* test)
{
m_test = test;
emit testChanged();
}
TestObject2* test() {
return m_test;
}
signals:
void TestStringChanged();
void testChanged();
private:
QString m_TestString;
TestObject2* m_test;
};
Q_DECLARE_METATYPE(TestObject*)
#endif // TESTOBJECT
#ifndef TESTOBJECT2
#define TESTOBJECT2
#include
class TestObject2 : public QObject
{
Q_OBJECT
Q_PROPERTY(QString TestString READ TestString WRITE setTestString NOTIFY TestStringChanged)
public:
Q_INVOKABLE explicit TestObject2(QObject *parent = 0) : QObject(parent) {}
public:
QString TestString() const
{ return m_TestString; }
public slots:
void setTestString(QString TestString)
{
if(this->m_TestString != TestString)
{
this->m_TestString = TestString;
emit TestStringChanged();
}
}
signals:
void TestStringChanged();
private:
QString m_TestString;
};
Q_DECLARE_METATYPE(TestObject2*)
#endif // TESTOBJECT2
To teraz czas na demo:
TestObject* test = new TestObject();
test->setTestString("root object string");
TestObject2* innerObject = new TestObject2();
innerObject->setTestString("inner object string");
test->settest(innerObject);
//DEMO 1 - Serializacja raz jeszcze
QDomDocument testDoc = QXmlSerialization::Serialize(test);
qDebug() << testDoc.toString();
//DEMO 2 - Deserializacja!
TestObject* test2 = new TestObject();
QXmlSerialization::Deserialize(testDoc, test2);
qDebug() << test2->test()->TestString();
Wynik wywołania demo, zgodnie z oczekiwaniami, wygląda następująco:

Known issues:
- Serializacja list w najnowszej wersji działa, pod warunkiem, że listą jest QVariantList.
- Deserializacja list działa dla typów zmiennych, nie zdefiniowanych przez użytkownika
- Dla typów zdefiniowanych przez użytkownika deserializacja list działa tylko pod Windowsem
To tyle w kwestii zarówno serializacji jak i deserializacji. Może kiedyś, jak przyjdą lepsze czasy to projekt uaktualnię. Następny wpis, zgodnie z tym, co napisałem na samym początku, poświęcę już w 100% C# 😉
Ziomek od lipca nie ma nic nowego?! Publiczność żąda artykułów!
dziś coś wrzucę!