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
-
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(); //childrenwriter.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.
-
[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.