QT 反射之Xml序列化学习

经过之前对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才行,所以反序列化失败。

如果有同行知道如何解决的,望阁下
047D12A8.jpg
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容