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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容