Reading and writing QObjects to/from XML



  • I'm new to QT and, as an exercise to familiarize my way around Qt's meta objects, I thought I'd try to write a class to read and write QObject to/from XML files.

    What I'd like is to pass an object to a function and have that object written to an XML file, along with any child objects (single or in lists). The same for reading the object back in.

    Something like:

    MyXMLSerializer::WriteToXML(QObject* myObject, QString filePath);
    MyXMLSerializer::ReadFromXML(QObject* myObject, QString filePath);

    Using Q_PROPERTIES/QMetaProperty in the objects and QXmlStreamReader and QXmlStreamWriter it's easy enough to read/write an object's basic properties (QString, int, double, etc.) to/from XML, but I'm stuck trying to figure out how to read/write child objects and or child collections of objects.

    Could anyone offer a suggestion no how to go about handling these child objects?

    TIA


  • Moderators

    Hi, welcome to devnet.

    Sounds like you just need to call recursively your writing function. Something like this:
    @
    void MyXMLSerializer::WriteToXML(QObject* myObject, QString filePath) {
    QXmlStreamWriter writer;
    ... //open a file etc.
    writer.writeStartDocument();
    serializeObject(myObject, writer);
    writer.writeEndDocument();
    }

    void MyXMLSerializer::serializeObject(QObject* obj, QXMLStreamWriter& writer) {
    writer.writeStartElement(...);

    ... //write this object data

    writer.writeStartElement("children");
    auto children = obj-0>children();
    for(auto child : children)
    serializeObject(child, writer);
    writer.writeEndElement(); //children

    writer.writeEndElement();
    }
    @
    Similarly for reading.



  • Hi.
    i am not cleard to your problem .
    can you show me some xml file contetnts for QObject that you want to read and write.



  • Hi Chris,

    Thanks for your welcome and your reply.

    I follow everything you wrote, but what I don't understand is how to detect that that a property of the object that is being written, is a list or collection rather than a primitive field.

    I don't think I made myself entirely clear in my original post. I'd like to write a function to handle writing any QObject (with Q_PROPERTIES) to XML, not just one specific type.

    For example:

    @class Child
    {
    Q_PROPERTY(QString childName READ name....)
    ...
    }

    class Parent
    {
    Q_PROPERTY(QString parentName READ name...)
    Q_PROPERTY(Child child READ child...)
    Q_PROPERTY(List<Child> children READ children...)
    ...
    }

    // Construct a parent and add some children.
    Parent *p = new Parent();
    p->setName("Name");
    p->setChild(new Child());
    p->children.Add(new Child());
    p->children.Add(new Child());
    p->children.Add(new Child());

    // Write the parent and all its children to XML.
    MyXMLSerializer::WriteToXML(p, xmlFilePath);@

    The code writing the XML just gets a pointer to QObject and knows nothing of the object structure to be written other than what can be gleaned from meta data. Here's how I thought I'd handle it.

    @
    void MyXMLSerializer::serializeObject(QObject* obj, QXMLStreamWriter& writer) {
    // Iterate the meta data for the object's properties.
    int count = obj->metaObject()->propertyCount();
    for (int i = 0; i < count; i++){
    //Fetch the property meta data
    QMetaProperty metaproperty = obj->metaObject()->property(i);
    QString propertyName = metaproperty.name();

        // Write the property to XML - Fine for 
        // strings, ints, etc, but how to handle other 
        // QObjects or object lists?
        QString data = obj->property( propertyName.toUtf8()).toString();
        xml.writeTextElement(propertyName, data);
    }
    

    }
    @

    I could test each property's type in the loop (QMetaProperty::type()), but I'm not sure how to detect child objects or lists for special handling.

    I hope that makes sense.


  • Moderators

    [quote author="steve61" date="1415099334"] @
    // Construct a parent and add some children.
    Parent *p = new Parent();
    p->setName("Name");
    p->setChild(new Child());
    p->children.Add(new Child());
    p->children.Add(new Child());
    p->children.Add(new Child());
    @

    [/quote]

    That's not how you set a parent-child relation with QObjects. Should be:
    @
    Parent *p = new Parent();

    //pass a parent in the constructor
    new Child(p);

    //or set a parent later
    auto child = new Child();
    child->setParent(p);

    // this will get a read-only list of the children, you can't append to it
    auto children = p->children();
    @
    Having that you can get the list of children like I showed you.

    [quote author="steve61" date="1415099334"]I could test each property’s type in the loop (QMetaProperty::type()), but I’m not sure how to detect child objects or lists for special handling. [/quote]
    Ok, don't confuse "properties" and "child objects". These are two different things. For children I gave you the solution in the previous post.

    As for properties you can't simply convert a property to string like you did. Some types are not convertible like that eg. pointer will convert to a string because it's just a number (an address in memory) but it's useless.

    You can do something like this:
    @
    auto cnt = obj->metaObject()->propertyCount();
    for(int i = 0; i < cnt; ++i) {
    auto metaProp = obj->metaObject()->property(i);
    auto propName = metaProp.name();
    auto prop = metaProp.read(obj);
    QString data;
    switch(metaProp.type()) {
    //try to convert QVariant to string by default:
    default: data = prop.toString(); break;
    //but handle other types explicitly:
    case QMetaType::QSize:
    data = QString::number(prop.toSize().width()) + ", "
    + QString::number(prop.toSize().height());
    break;
    //and so on
    case ...
    }
    xml.writeTextElement(propertyName, data);
    }
    @

    Notice that you will have to handle all the "unusual" types yourself, because there' s no way to automatically convert "anything" to sensible string representation in a generic way. We're not in Java world ;)



  • All understood Chris.
    Thanks for your help.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.