经过之前对QMetaObject的学习与了解,尝试用元对象的方式对自定义的对象进行序列化到Xml中。
1.声明自定义类
Student,继承于QObject 并添加Q_OBJECT宏命令
class Student : public QObject
{
Q_OBJECT
Q_PROPERTY(int age READ getAge WRITE setAge)
Q_PROPERTY(QString name READ getName WRITE setName)
Q_PROPERTY(QDateTime birthday READ getBirthday WRITE setBirthday)
Q_PROPERTY(Grade* grade READ getGrade WRITE setGrade)
Q_PROPERTY(QList<Book*> books READ getBooks WRITE setBooks)
private:
int age = 0;
QString name = "";
QDateTime birthday;
Grade* grade = nullptr;
QList<Book*> books;
public:
explicit Student(QObject *parent = nullptr);
int getAge();
QString getName();
QDateTime getBirthday();
Grade* getGrade();
QList<Book*> getBooks();
void setAge(int a);
void setName(QString n);
void setBirthday(QDateTime d);
void setGrade(Grade* g);
void setBooks(QList<Book*> book);
};
子对象Grade 继承于QObject 并添加Q_OBJECT宏命令
class Grade : public QObject
{
Q_OBJECT
Q_PROPERTY(QString gradeName READ getGradeName WRITE setGradeName)
private:
QString gradeName;
public:
explicit Grade(QObject *parent = nullptr);
QString getGradeName();
void setGradeName(QString name);
signals:
};
子对象Book 继承于QObject 并添加Q_OBJECT宏命令
class Book : public QObject
{
Q_OBJECT
Q_PROPERTY(QString bookName READ getBookName WRITE setBookName)
private:
QString bookName;
public:
explicit Book(QObject *parent = nullptr);
QString getBookName();
void setBookName(QString name);
signals:
};
函数的实现代码较简单未贴出。
2.实现序列化对象方法
根据元对象遍历对象的属性,然后序列化属性
QString ObjectSerial::serializeToXml(QObject* object)
{
QString xml;
QXmlStreamWriter xmlWriter(&xml);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
const QMetaObject* metaObject = object->metaObject();
xmlWriter.writeStartElement(metaObject->className());
int propertyCount = metaObject->propertyCount();
for (int i = 0; i < propertyCount; ++i) {
QMetaProperty property = metaObject->property(i);
serializeProperty(xmlWriter, property, object);
}
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
return xml;
}
实现序列化属性方法
void ObjectSerial::serializeProperty(QXmlStreamWriter& xmlWriter, const QMetaProperty& property, QObject* object)
{
const QVariant propertyValue = property.read(object);
QString typeName = property.typeName();
if (propertyValue.canConvert<QObject*>()) {
QObject* childObject = qvariant_cast<QObject*>(propertyValue);
serialObject(xmlWriter,childObject);
}else if (typeName.contains("QList<")||typeName.contains("QVector<")) {
QSequentialIterable iterable = propertyValue.value<QSequentialIterable>();
QString listName = typeName.replace('<','_').replace('>','-').replace('*','.');
xmlWriter.writeStartElement(listName);
for (const QVariant& item : iterable) {
QObject* childObject = qvariant_cast<QObject*>(item);
serialObject(xmlWriter,childObject);
}
xmlWriter.writeEndElement();
}else if (propertyValue.canConvert<QList<QObject*>>()) {
QList<QObject*> childObjects = propertyValue.value<QList<QObject*>>();
foreach (QObject* childObject, childObjects) {
serialObject(xmlWriter,childObject);
}
} else {
xmlWriter.writeTextElement(property.name(), propertyValue.toString());
}
}
函数说明:
判断元对象类型,若是QObject则进行递归序列化对象。
若是其他集合类型进行单独处理,可自行扩展QMap、QHash等其他集合对象的处理
若是简单属性则直接写入Xml
由于<>、属于Xml中的关键标签所以进行了替换
3.调用测试
Student student;
student.setAge(15);
student.setName("张三");
student.setBirthday(QDateTime(QDate(1990,7,1)));
Grade g;
g.setGradeName("三年纪");
student.setGrade(&g);
Book b1;
b1.setBookName("西游记");
Book b2;
b2.setBookName("红楼梦");
QList<Book*> books;
books << &b1 << &b2;
student.setBooks(books);
QObject* obj = dynamic_cast<QObject*>(&student);
QString xml = ObjectSerial::serializeToXml(obj);
qDebug() << xml;
4.输出结果
<?xml version=\"1.0\"?>\n
<Student>\n
<objectName></objectName>\n
<age>15</age>\n
<name>张三</name>\n
<birthday>1990-07-01T00:00:00.000</birthday>\n
<Grade>\n
<objectName></objectName>\n
<gradeName>三年纪</gradeName>\n
</Grade>\n
<QList_Book.->\n
<Book>\n
<objectName></objectName>\n
<bookName>西游记</bookName>\n
</Book>\n
<Book>\n
<objectName></objectName>\n
<bookName>红楼梦</bookName>\n
</Book>\n
</QList_Book.->\n
</Student>\n"
5.总结
1.通过元对象的方式确实可以对自定义的类进行通用的方式序列化到xml中
2.不过对于一些特殊的对象需要自行扩展,但是也能适应大部分情况。
6.疑问
在尝试反序列化时,没有成功。
因为xml中存储的是类型的名称,而通过类型名称获取元对象或者或许类型ID是都失败了。
比如获取Student的typeId:
int typeId = QMetaType::type("Student");
结果 typeId = 0;
尝试获取QT自带的类QPushButton的类型ID
int typeId = QMetaType::type("QPushButton");
结果依然是 typeId = 0;
然而创建对象的方式诸如:
QObject* object = qMetaTypeCreate(typeId);
metaObject.newInstance();
都是需要元对象或者类型ID才行,所以反序列化失败。