QXmlSerialization [Część 2. z 2. – Deserializacja]

W ciągu ostatniego tygodnia projekt zdążył nieco wyewoluować. Proces deserializaji opiszę zatem w takiej formie, w jakiej istnieje obecnie (poza jedną, małą zmianą).

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: XML test

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# 😉

2 thoughts on “QXmlSerialization [Część 2. z 2. – Deserializacja]

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *