Qt中Json的序列化反序列化

QJsonDocument 以及相关的 JSON 类 (QJsonObject, QJsonArray, QJsonValue, QJsonParseError) 是在 Qt 5.0 中首次引入的。因此,Qt 5.0 是首次包含这些 JSON 处理功能的版本。在此之前,Qt 没有内置的 JSON 支持,开发人员通常依赖第三方库或自己的实现来处理 JSON 数据。

1、QJsonDoucment

使用QJsonDocument来对Json的解析已经很方便了, 通常的做法如下:

  • 序列化(将数据转化为JSON字符串)

    #include <QJsonDocument>
    #include <QJsonObject>
    #include <QJsonArray>
    #include <QDebug>
    
    int main() {
        // 创建一个QJsonObject对象
        QJsonObject jsonObject;
        jsonObject["name"] = "Alice";
        jsonObject["age"] = 30;
    
        // 创建一个QJsonArray对象
        QJsonArray jsonArray;
        jsonArray.append("Reading");
        jsonArray.append("Traveling");
        jsonObject["hobbies"] = jsonArray;
    
        // 创建一个QJsonDocument对象
        QJsonDocument jsonDoc(jsonObject);
    
        // 将QJsonDocument对象转化为JSON字符串
        QByteArray jsonData = jsonDoc.toJson();
    
        qDebug() << jsonData;
        return 0;
    }
    
  • 反序列化(将JSON字符串转化为数据)

    #include <QJsonDocument>
    #include <QJsonObject>
    #include <QJsonArray>
    #include <QDebug>
    
    int main() {
        QByteArray jsonData = R"({
            "name": "Alice",
            "age": 30,
            "hobbies": ["Reading", "Traveling"]
        })";
    
        // 从JSON字符串中解析QJsonDocument对象
        QJsonParseError error;
        QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
    
        if (error.error != QJsonParseError::NoError) {
            qDebug() << "JSON parse error:" << error.errorString();
            return -1;
        }
    
        // 获取数据
        QJsonObject jsonObject = jsonDoc.object();
        QString name = jsonObject["name"].toString();
        int age = jsonObject["age"].toInt();
        QJsonArray hobbies = jsonObject["hobbies"].toArray();
    
        qDebug() << "Name:" << name;
        qDebug() << "Age:" << age;
        qDebug() << "Hobbies:" << hobbies;
    
        return 0;
    }
    

可以看到对于这种简单的对象是很容做到通用化的序列/反序列化封装的,但是实际情况下还是存在自定义类型的字段和自定义类型的集合,更复杂的情况下还存在嵌套

  • 简单的自定义类型的序列反序列化的通常做法:

    #include <QJsonDocument>
    #include <QJsonObject>
    #include <QDebug>
    
    class Person {
    public:
        Person() = default;
        Person(const QString &name, int age) : m_name(name), m_age(age) {}
    
        QString name() const { return m_name; }
        int age() const { return m_age; }
    
        void setName(const QString &name) { m_name = name; }
        void setAge(int age) { m_age = age; }
    
        // 转换为 QJsonObject
        QJsonObject toJson() const {
            QJsonObject jsonObj;
            jsonObj["name"] = m_name;
            jsonObj["age"] = m_age;
            return jsonObj;
        }
    
        // 从 QJsonObject 转换
        void fromJson(const QJsonObject &jsonObj) {
            m_name = jsonObj["name"].toString();
            m_age = jsonObj["age"].toInt();
        }
    
    private:
        QString m_name;
        int m_age;
    };
    
    int main() {
        // 创建一个 Person 对象
        Person person("Alice", 30);
    
        // 序列化
        QJsonObject jsonObj = person.toJson();
        QJsonDocument jsonDoc(jsonObj);
        QByteArray jsonData = jsonDoc.toJson();
        qDebug() << jsonData;
    
        // 反序列化
        QByteArray receivedData = jsonData;  // 假设这是从某处接收到的数据
        QJsonDocument receivedDoc = QJsonDocument::fromJson(receivedData);
        QJsonObject receivedJsonObj = receivedDoc.object();
        
        Person receivedPerson;
        receivedPerson.fromJson(receivedJsonObj);
        qDebug() << "Name:" << receivedPerson.name();
        qDebug() << "Age:" << receivedPerson.age();
    
        return 0;
    }
    

2、三方库QJson

QJson

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It can represents integer, real number, string, an ordered sequence of value, and a collection of name/value pairs.

QJson is a qt-based library that maps JSON data to QVariant objects. JSON arrays will be mapped to QVariantList instances, while JSON's objects will be mapped to QVariantMap.


QJson requires:

  • Qt 4.5 or greater
  • cmake 2.6 or greater

使用过一段时间现实比较复杂,需要符合QVariant中对Meta Object的一些要求,对于自定义类型的嵌套还是具备一定的复杂性

3、推荐三方库QSerializer

This project is designed to convert data from an object view to JSON or XML and opposite in the Qt/C++ ecosystem. C ++ classes by default do not have the required meta-object information for serializing class fields, but Qt is equipped with its own highly efficient meta-object system. An important feature of the QSerializer is the ability to specify serializable fields of the class without having to serialize the entire class. QSerilaizer generate code and declare Q_PROPERTY for every declared member of class. This is convenient because you do not need to create separate structures or classes for serialization of write some code to serialize every class, it's just included to QSerializer.

单一文件实现,使用简单,代码的可读性强,由于实现代码不长直接贴出来:

/*
 MIT License

 Copyright (c) 2020-2021 Agadzhanov Vladimir
 Portions Copyright (c) 2021 Jerry Jacobs

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
                                                                                 */

#ifndef QSERIALIZER_H
#define QSERIALIZER_H

/* JSON */
#ifdef QS_HAS_JSON
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonValue>
#endif

/* XML */
#ifdef QS_HAS_XML
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#endif

/* META OBJECT SYSTEM */
#include <QVariant>
#include <QMetaProperty>
#include <QMetaObject>
#include <QMetaType>

#include <type_traits>
#include <QDebug>

#define QS_VERSION "1.2"

/* Generate metaObject method */
#define QS_META_OBJECT_METHOD \
    virtual const QMetaObject * metaObject() const { \
        return &this->staticMetaObject; \
    } \

#define QSERIALIZABLE \
    Q_GADGET \
    QS_META_OBJECT_METHOD

/* Mark class as serializable */
#define QS_SERIALIZABLE QS_META_OBJECT_METHOD

#ifdef QS_HAS_XML
Q_DECLARE_METATYPE(QDomNode)
Q_DECLARE_METATYPE(QDomElement)
#endif

class QSerializer {
    Q_GADGET
    QS_SERIALIZABLE
public:
    virtual ~QSerializer() = default;

#ifdef QS_HAS_JSON
    /*! \brief  Convert QJsonValue in QJsonDocument as QByteArray. */
    static QByteArray toByteArray(const QJsonValue & value){
        return QJsonDocument(value.toObject()).toJson();
    }
#endif

#ifdef QS_HAS_XML
    /*! \brief  Convert QDomNode in QDomDocument as QByteArray. */
    static QByteArray toByteArray(const QDomNode & value) {
        QDomDocument doc = value.toDocument();
        return doc.toByteArray();
    }

    /*! \brief  Make xml processing instruction (hat) and returns new XML QDomDocument. On deserialization procedure all processing instructions will be ignored. */
    static QDomDocument appendXmlHat(const QDomNode &node, const QString & encoding, const QString & version = "1.0"){
        QDomDocument doc = node.toDocument();
        QDomNode xmlNode = doc.createProcessingInstruction("xml", QString("version=\"%1\" encoding=\"%2\"").arg(version).arg(encoding));
        doc.insertBefore(xmlNode, doc.firstChild());
        return doc;
    }
#endif


#ifdef QS_HAS_JSON
    /*! \brief  Serialize all accessed JSON propertyes for this object. */
    QJsonObject toJson() const {
        QJsonObject json;
        for(int i = 0; i < metaObject()->propertyCount(); i++)
        {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
            if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
                continue;
            }
#else
            if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
                continue;
            }
#endif

            json.insert(metaObject()->property(i).name(), metaObject()->property(i).readOnGadget(this).toJsonValue());
        }
        return json;
    }

    /*! \brief  Returns QByteArray representation this object using json-serialization. */
    QByteArray toRawJson() const {
        return toByteArray(toJson());
    }

    /*! \brief  Deserialize all accessed XML propertyes for this object. */
    void fromJson(const QJsonValue & val) {
        if(val.isObject())
        {
            QJsonObject json = val.toObject();
            QStringList keys = json.keys();
            int propCount = metaObject()->propertyCount();
            for(int i = 0; i < propCount; i++)
            {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
                if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
                    continue;
                }
#else
                if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
                    continue;
                }
#endif

                for(auto key : json.keys())
                {
                    if(key == metaObject()->property(i).name())
                    {
                        metaObject()->property(i).writeOnGadget(this, json.value(key));
                        break;
                    }
                }
            }
        }
    }

    /*! \brief  Deserialize all accessed JSON propertyes for this object. */
    void fromJson(const QByteArray & data) {
        fromJson(QJsonDocument::fromJson(data).object());
    }
#endif // QS_HAS_JSON

#ifdef QS_HAS_XML
    /*! \brief  Serialize all accessed XML propertyes for this object. */
    QDomNode toXml() const {
        QDomDocument doc;
        QDomElement el = doc.createElement(metaObject()->className());
        for(int i = 0; i < metaObject()->propertyCount(); i++)
        {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
            if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QDomNode>())) {
                continue;
            }
#else
            if(metaObject()->property(i).metaType().id() != qMetaTypeId<QDomNode>()) {
                continue;
            }
#endif
            el.appendChild(QDomNode(metaObject()->property(i).readOnGadget(this).value<QDomNode>()));
        }
        doc.appendChild(el);
        return doc;
    }

    /*! \brief  Returns QByteArray representation this object using xml-serialization. */
    QByteArray toRawXml() const {
        return toByteArray(toXml());
    }

    /*! \brief  Deserialize all accessed XML propertyes for this object. */
    void fromXml(const QDomNode & val) {
        QDomNode doc = val;

        auto n = doc.firstChildElement(metaObject()->className());

        if(!n.isNull()) {
            for(int i = 0; i < metaObject()->propertyCount(); i++) {
                QString name = metaObject()->property(i).name();
                QDomElement tmp = metaObject()->property(i).readOnGadget(this).value<QDomNode>().firstChildElement();

                auto f = n.firstChildElement(tmp.tagName());
                metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
            }
        }
        else
        {
            for(int i = 0; i < metaObject()->propertyCount(); i++) {
                QString name = metaObject()->property(i).name();
                auto f = doc.firstChildElement(name);
                metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
            }
        }
    }

    /*! \brief  Deserialize all accessed XML propertyes for this object. */
    void fromXml(const QByteArray & data) {
        QDomDocument d;
        d.setContent(data);
        fromXml(d);
    }
#endif // QS_HAS_XML
};

#define GET(prefix, name) get_##prefix##_##name
#define SET(prefix, name) set_##prefix##_##name

/* Create variable */
#define QS_DECLARE_MEMBER(type, name)                                                       \
    public :                                                                                \
    type name = type();                                                                     \

/* Create JSON property and methods for primitive type field*/
#ifdef QS_HAS_JSON
#define QS_JSON_FIELD(type, name)                                                           \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name))                  \
    private:                                                                                \
        QJsonValue GET(json, name)() const {                                                \
            QJsonValue val = QJsonValue::fromVariant(QVariant(name));                       \
            return val;                                                                     \
        }                                                                                   \
        void SET(json, name)(const QJsonValue & varname){                                   \
            name = varname.toVariant().value<type>();                                       \
        }                                                                                   
#else
#define QS_JSON_FIELD(type, name)
#endif

/* Create XML property and methods for primitive type field*/
#ifdef QS_HAS_XML
#define QS_XML_FIELD(type, name)                                                            \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                      \
    private:                                                                                \
    QDomNode GET(xml, name)() const {                                                       \
        QDomDocument doc;                                                                   \
        QString strname = #name;                                                            \
        QDomElement element = doc.createElement(strname);                                   \
        QDomText valueOfProp = doc.createTextNode(QVariant(name).toString());               \
        element.appendChild(valueOfProp);                                                   \
        doc.appendChild(element);                                                           \
        return  QDomNode(doc);                                                              \
    }                                                                                       \
    void SET(xml, name)(const QDomNode &node) {                                             \
        if(!node.isNull() && node.isElement()){                                             \
            QDomElement domElement = node.toElement();                                      \
            if(domElement.tagName() == #name)                                               \
                name = QVariant(domElement.text()).value<type>();                           \
        }                                                                                   \
    }                                                                                       
#else
#define QS_XML_FIELD(type, name)
#endif

/* Generate JSON-property and methods for primitive type objects */
/* This collection must be provide method append(T) (it's can be QList, QVector)    */
#ifdef QS_HAS_JSON
#define QS_JSON_ARRAY(itemType, name)                                                       \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name))                  \
    private:                                                                                \
        QJsonValue GET(json, name)() const {                                                \
            QJsonArray val;                                                                 \
            for(int i = 0; i < name.size(); i++)                                            \
                val.push_back(name.at(i));                                                  \
            return QJsonValue::fromVariant(val);                                            \
        }                                                                                   \
        void SET(json, name)(const QJsonValue & varname) {                                  \
            if(!varname.isArray())                                                          \
                return;                                                                     \
            name.clear();                                                                   \
            QJsonArray val = varname.toArray();                                             \
            for(auto item : val) {                                                          \
                itemType tmp;                                                               \
                tmp = item.toVariant().value<itemType>();                                   \
                name.append(tmp);                                                           \
            }                                                                               \
        }                                                                                
#else
#define QS_JSON_ARRAY(itemType, name)
#endif

/* Generate XML-property and methods for primitive type objects */
/* This collection must be provide method append(T) (it's can be QList, QVector)    */
#ifdef QS_HAS_XML
#define QS_XML_ARRAY(itemType, name)                                                        \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                      \
    private:                                                                                \
        QDomNode GET(xml, name)() const {                                                   \
            QDomDocument doc;                                                               \
            QString strname = #name;                                                        \
            QDomElement arrayXml = doc.createElement(QString(strname));                     \
            arrayXml.setAttribute("type", "array");                                         \
                                                                                            \
            for(int i = 0; i < name.size(); i++) {                                          \
                itemType item = name.at(i);                                                 \
                QDomElement itemXml = doc.createElement("item");                            \
                itemXml.setAttribute("type", #itemType);                                    \
                itemXml.setAttribute("index", i);                                           \
                itemXml.appendChild(doc.createTextNode(QVariant(item).toString()));         \
                arrayXml.appendChild(itemXml);                                              \
            }                                                                               \
                                                                                            \
            doc.appendChild(arrayXml);                                                      \
            return  QDomNode(doc);                                                          \
        }                                                                                   \
        void SET(xml, name)(const QDomNode & node) {                                        \
            QDomNode domNode = node.firstChild();                                           \
            name.clear();                                                                   \
            while(!domNode.isNull()) {                                                      \
                if(domNode.isElement()) {                                                   \
                    QDomElement domElement = domNode.toElement();                           \
                    name.append(QVariant(domElement.text()).value<itemType>());             \
                }                                                                           \
                domNode = domNode.nextSibling();                                            \
            }                                                                               \
        }
#else
#define QS_XML_ARRAY(itemType, name)
#endif


/* Generate JSON-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
#ifdef QS_HAS_JSON
#define QS_JSON_OBJECT(type, name)                                                          \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name))                  \
    private:                                                                                \
    QJsonValue GET(json, name)() const {                                                    \
        QJsonObject val = name.toJson();                                                    \
        return QJsonValue(val);                                                             \
    }                                                                                       \
    void SET(json, name)(const QJsonValue & varname) {                                      \
        if(!varname.isObject())                                                             \
        return;                                                                             \
        name.fromJson(varname);                                                             \
    }
#else
#define QS_JSON_OBJECT(type, name)
#endif

/* Generate XML-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
#ifdef QS_HAS_XML
#define QS_XML_OBJECT(type, name)                                                           \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                      \
    private:                                                                                \
        QDomNode GET(xml, name)() const {                                                   \
            return name.toXml();                                                            \
        }                                                                                   \
        void SET(xml, name)(const QDomNode & node){                                         \
            name.fromXml(node);                                                             \
        }                                                                                   
#else
#define QS_XML_OBJECT(type, name)
#endif

/* Generate JSON-property and methods for collection of custom type objects */
/* Custom item type must be provide methods fromJson and toJson or inherit from QSerializer */
/* This collection must be provide method append(T) (it's can be QList, QVector)    */
#ifdef QS_HAS_JSON
#define QS_JSON_ARRAY_OBJECTS(itemType, name)                                               \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name))                  \
    private:                                                                                \
        QJsonValue GET(json, name)() const {                                                \
            QJsonArray val;                                                                 \
            for(int i = 0; i < name.size(); i++)                                            \
                val.push_back(name.at(i).toJson());                                         \
            return QJsonValue::fromVariant(val);                                            \
        }                                                                                   \
        void SET(json, name)(const QJsonValue & varname) {                                  \
            if(!varname.isArray())                                                          \
                return;                                                                     \
            name.clear();                                                                   \
            QJsonArray val = varname.toArray();                                             \
            for(int i = 0; i < val.size(); i++) {                                           \
                itemType tmp;                                                               \
                tmp.fromJson(val.at(i));                                                    \
                name.append(tmp);                                                           \
            }                                                                               \
        }                                                                                   
#else
#define QS_JSON_ARRAY_OBJECTS(itemType, name)
#endif

/* Generate XML-property and methods for collection of custom type objects  */
/* Custom type must be provide methods fromXml and toXml or inherit from QSerializer */
/* This collection must be provide method append(T) (it's can be QList, QVector)    */
#ifdef QS_HAS_XML
#define QS_XML_ARRAY_OBJECTS(itemType, name)                                                \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                      \
    private:                                                                                \
    QDomNode GET(xml, name)() const {                                                       \
        QDomDocument doc;                                                                   \
        QDomElement element = doc.createElement(#name);                                     \
        element.setAttribute("type", "array");                                              \
        for(int i = 0; i < name.size(); i++)                                                \
            element.appendChild(name.at(i).toXml());                                        \
        doc.appendChild(element);                                                           \
        return QDomNode(doc);                                                               \
    }                                                                                       \
    void SET(xml, name)(const QDomNode & node) {                                            \
        name.clear();                                                                       \
        QDomNodeList nodesList = node.childNodes();                                         \
        for(int i = 0;  i < nodesList.size(); i++) {                                        \
            itemType tmp;                                                                   \
            tmp.fromXml(nodesList.at(i));                                                   \
            name.append(tmp);                                                               \
        }                                                                                   \
    }                                                                                       
#else
#define QS_XML_ARRAY_OBJECTS(itemType, name)
#endif

/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...)  */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash)    */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
#ifdef QS_HAS_JSON
#define QS_JSON_QT_DICT(map, name)                                                          \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name))                   \
    private:                                                                                \
    QJsonValue GET(json, name)() const {                                                    \
        QJsonObject val;                                                                    \
        for(auto p = name.constBegin(); p != name.constEnd(); ++p) {                        \
            val.insert(                                                                     \
                QVariant(p.key()).toString(),                                               \
                QJsonValue::fromVariant(QVariant(p.value())));                              \
        }                                                                                   \
        return val;                                                                         \
    }                                                                                       \
    void SET(json, name)(const QJsonValue & varname) {                                      \
        QJsonObject val = varname.toObject();                                               \
        name.clear();                                                                       \
        for(auto p = val.constBegin() ;p != val.constEnd(); ++p) {                          \
            name.insert(                                                                    \
                QVariant(p.key()).value<map::key_type>(),                                   \
                QVariant(p.value()).value<map::mapped_type>());                             \
        }                                                                                   \
    }                                                                                       
#else
#define QS_JSON_QT_DICT(map, name)
#endif

/* Generate XML-property and methods for dictionary of simple fields (int, bool, QString, ...)  */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash)    */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
#ifdef QS_HAS_XML
#define QS_XML_QT_DICT(map, name)                                                                       \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                                  \
    private:                                                                                            \
    QDomNode GET(xml, name)() const {                                                                   \
        QDomDocument doc;                                                                               \
        QDomElement element = doc.createElement(#name);                                                 \
        element.setAttribute("type", "map");                                                            \
        for(auto p = name.begin(); p != name.end(); ++p)                                                \
        {                                                                                               \
            QDomElement e = doc.createElement("item");                                                  \
            e.setAttribute("key", QVariant(p.key()).toString());                                        \
            e.setAttribute("value", QVariant(p.value()).toString());                                    \
            element.appendChild(e);                                                                     \
        }                                                                                               \
        doc.appendChild(element);                                                                       \
        return QDomNode(doc);                                                                           \
    }                                                                                                   \
    void SET(xml, name)(const QDomNode & node) {                                                        \
        if(!node.isNull() && node.isElement())                                                          \
        {                                                                                               \
            QDomElement root = node.toElement();                                                        \
            if(root.tagName() == #name)                                                                 \
            {                                                                                           \
                QDomNodeList childs = root.childNodes();                                                \
                                                                                                        \
                for(int i = 0; i < childs.size(); ++i) {                                                \
                QDomElement item = childs.at(i).toElement();                                            \
                name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(),         \
                            QVariant(item.attributeNode("value").value()).value<map::mapped_type>());   \
                }                                                                                       \
            }                                                                                           \
        }                                                                                               \
    }                                                                                                   
#else
#define QS_XML_QT_DICT(map, name)
#endif


/* Generate JSON-property and methods for dictionary of custom type objects  */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method inserv(KeyT, ValueT) (it's can be QMap, QHash)    */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
#ifdef QS_HAS_JSON
#define QS_JSON_QT_DICT_OBJECTS(map, name)                                                  \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name))                   \
    private:                                                                                \
    QJsonValue GET(json, name)() const {                                                    \
        QJsonObject val;                                                                    \
        for(auto p = name.begin(); p != name.end(); ++p) {                                  \
            val.insert(                                                                     \
                QVariant::fromValue(p.key()).toString(),                                    \
                p.value().toJson());                                                        \
        }                                                                                   \
        return val;                                                                         \
    }                                                                                       \
    void SET(json, name)(const QJsonValue & varname) {                                      \
        QJsonObject val = varname.toObject();                                               \
        name.clear();                                                                       \
        for(auto p = val.constBegin();p != val.constEnd(); ++p) {                           \
        map::mapped_type tmp;                                                               \
        tmp.fromJson(p.value());                                                            \
        name.insert(                                                                        \
                QVariant(p.key()).value<map::key_type>(),                                   \
                tmp);                                                                       \
        }                                                                                   \
    }                                                                                       
#else
#define QS_JSON_QT_DICT_OBJECTS(map, name)
#endif

/* Generate XML-property and methods for dictionary of custom type objects  */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash)    */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
#ifdef QS_HAS_XML
#define QS_XML_QT_DICT_OBJECTS(map, name)                                                               \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                                  \
    private:                                                                                            \
    QDomNode GET(xml, name)() const {                                                                   \
        QDomDocument doc;                                                                               \
        QDomElement element = doc.createElement(#name);                                                 \
        element.setAttribute("type", "map");                                                            \
        for(auto p = name.begin(); p != name.end(); ++p)                                                \
        {                                                                                               \
            QDomElement e = doc.createElement("item");                                                  \
            e.setAttribute("key", QVariant(p.key()).toString());                                        \
            e.appendChild(p.value().toXml());                                                           \
            element.appendChild(e);                                                                     \
        }                                                                                               \
        doc.appendChild(element);                                                                       \
        return QDomNode(doc);                                                                           \
    }                                                                                                   \
    void SET(xml, name)(const QDomNode & node) {                                                        \
        if(!node.isNull() && node.isElement())                                                          \
        {                                                                                               \
            QDomElement root = node.toElement();                                                        \
            if(root.tagName() == #name)                                                                 \
            {                                                                                           \
                QDomNodeList childs = root.childNodes();                                                \
                                                                                                        \
                for(int i = 0; i < childs.size(); ++i) {                                                \
                QDomElement item = childs.at(i).toElement();                                            \
                map::mapped_type tmp;                                                                   \
                tmp.fromXml(item.firstChild());                                                         \
                name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(),         \
                            tmp);                                                                       \
                }                                                                                       \
            }                                                                                           \
        }                                                                                               \
    }                                                                                                   
#else
#define QS_XML_QT_DICT_OBJECTS(map, name)
#endif

/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...)  */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map)    */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, QString>, std::map<int,int>, ...*/
#ifdef QS_HAS_JSON
#define QS_JSON_STL_DICT(map, name)                                                         \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name))                   \
    private:                                                                                \
    QJsonValue GET(json, name)() const {                                                    \
        QJsonObject val;                                                                    \
        for(auto p : name){                                                                 \
            val.insert(                                                                     \
                QVariant::fromValue(p.first).toString(),                                    \
                QJsonValue::fromVariant(QVariant(p.second)));                               \
        }                                                                                   \
        return val;                                                                         \
    }                                                                                       \
    void SET(json, name)(const QJsonValue & varname) {                                      \
        QJsonObject val = varname.toObject();                                               \
        name.clear();                                                                       \
        for(auto p = val.constBegin();p != val.constEnd(); ++p) {                           \
            name.insert(std::pair<map::key_type, map::mapped_type>(                         \
                QVariant(p.key()).value<map::key_type>(),                                   \
                QVariant(p.value()).value<map::mapped_type>()));                            \
        }                                                                                   \
    }                                                                                       
#else
#define QS_JSON_STL_DICT(map, name)
#endif

#ifdef QS_HAS_XML
#define QS_XML_STL_DICT(map, name)                                                                       \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                                  \
    private:                                                                                            \
    QDomNode GET(xml, name)() const {                                                                   \
        QDomDocument doc;                                                                               \
        QDomElement element = doc.createElement(#name);                                                 \
        element.setAttribute("type", "map");                                                            \
        for(auto p : name)                                                                              \
        {                                                                                               \
            QDomElement e = doc.createElement("item");                                                  \
            e.setAttribute("key", QVariant(p.first).toString());                                        \
            e.setAttribute("value", QVariant(p.second).toString());                                     \
            element.appendChild(e);                                                                     \
        }                                                                                               \
        doc.appendChild(element);                                                                       \
        return QDomNode(doc);                                                                           \
    }                                                                                                   \
    void SET(xml, name)(const QDomNode & node) {                                                        \
        if(!node.isNull() && node.isElement())                                                          \
        {                                                                                               \
            QDomElement root = node.toElement();                                                        \
            if(root.tagName() == #name)                                                                 \
            {                                                                                           \
                QDomNodeList childs = root.childNodes();                                                \
                                                                                                        \
                for(int i = 0; i < childs.size(); ++i) {                                                \
                QDomElement item = childs.at(i).toElement();                                            \
                name.insert(std::pair<map::key_type, map::mapped_type>(                                 \
                            QVariant(item.attributeNode("key").value()).value<map::key_type>(),         \
                            QVariant(item.attributeNode("value").value()).value<map::mapped_type>()));  \
                }                                                                                       \
            }                                                                                           \
        }                                                                                               \
    }                                                                                                   
#else
#define QS_XML_STL_DICT(map, name)                                                                    
#endif

/* Generate JSON-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map)    */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
#ifdef QS_HAS_JSON
#define QS_JSON_STL_DICT_OBJECTS(map, name)                                                 \
    Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name))                   \
    private:                                                                                \
    QJsonValue GET(json, name)() const {                                                    \
        QJsonObject val;                                                                    \
        for(auto p : name){                                                                 \
            val.insert(                                                                     \
                QVariant::fromValue(p.first).toString(),                                    \
                p.second.toJson());                                                         \
        }                                                                                   \
        return val;                                                                         \
    }                                                                                       \
    void SET(json, name)(const QJsonValue & varname) {                                      \
        QJsonObject val = varname.toObject();                                               \
        name.clear();                                                                       \
        for(auto p = val.constBegin(); p != val.constEnd(); ++p) {                          \
            map::mapped_type tmp;                                                           \
            tmp.fromJson(p.value());                                                        \
            name.insert(std::pair<map::key_type, map::mapped_type>(                         \
                QVariant(p.key()).value<map::key_type>(),                                   \
                tmp));                                                                      \
        }                                                                                   \
    }                                                                                       
#else
#define QS_JSON_STL_DICT_OBJECTS(map, name)                               
#endif

/* Generate XML-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map)    */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
#ifdef QS_HAS_XML
#define QS_XML_STL_DICT_OBJECTS(map, name)                                                              \
    Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name))                                  \
    private:                                                                                            \
    QDomNode GET(xml, name)() const {                                                                   \
        QDomDocument doc;                                                                               \
        QDomElement element = doc.createElement(#name);                                                 \
        element.setAttribute("type", "map");                                                            \
        for(auto p : name)                                                \
        {                                                                                               \
            QDomElement e = doc.createElement("item");                                                  \
            e.setAttribute("key", QVariant(p.first).toString());                                        \
            e.appendChild(p.second.toXml());                                                            \
            element.appendChild(e);                                                                     \
        }                                                                                               \
        doc.appendChild(element);                                                                       \
        return QDomNode(doc);                                                                           \
    }                                                                                                   \
    void SET(xml, name)(const QDomNode & node) {                                                        \
        if(!node.isNull() && node.isElement())                                                          \
        {                                                                                               \
            QDomElement root = node.toElement();                                                        \
            if(root.tagName() == #name)                                                                 \
            {                                                                                           \
                QDomNodeList childs = root.childNodes();                                                \
                                                                                                        \
                for(int i = 0; i < childs.size(); ++i) {                                                \
                QDomElement item = childs.at(i).toElement();                                            \
                map::mapped_type tmp;                                                                   \
                tmp.fromXml(item.firstChild());                                                         \
                name.insert(std::pair<map::key_type, map::mapped_type> (                                \
                            QVariant(item.attributeNode("key").value()).value<map::key_type>(),         \
                            tmp));                                                                      \
                }                                                                                       \
            }                                                                                           \
        }                                                                                               \
    }                                                                                                   
#else
#define QS_XML_STL_DICT_OBJECTS(map, name)
#endif


/* BIND: */
/* generate serializable propertyes JSON and XML for primitive type field */
#define QS_BIND_FIELD(type, name)                                                           \
    QS_JSON_FIELD(type, name)                                                               \
    QS_XML_FIELD(type, name)                                                                \

/* BIND: */
/* generate serializable propertyes JSON and XML for collection of primitive type fields */
#define QS_BIND_COLLECTION(itemType, name)                                                  \
    QS_JSON_ARRAY(itemType, name)                                                           \
    QS_XML_ARRAY(itemType, name)                                                            \

/* BIND: */
/* generate serializable propertyes JSON and XML for custom type object */
#define QS_BIND_OBJECT(type, name)                                                          \
    QS_JSON_OBJECT(type, name)                                                              \
    QS_XML_OBJECT(type, name)                                                               \

/* BIND: */
/* generate serializable propertyes JSON and XML for collection of custom type objects */
#define QS_BIND_COLLECTION_OBJECTS(itemType, name)                                          \
    QS_JSON_ARRAY_OBJECTS(itemType, name)                                                   \
    QS_XML_ARRAY_OBJECTS(itemType, name)                                                    \

/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for QT DICTIONARY TYPES */
#define QS_BIND_QT_DICT(map, name)                                                         \
    QS_JSON_QT_DICT(map, name)                                                             \
    QS_XML_QT_DICT(map, name)                                                              \

/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for QT DICTIONARY TYPES */
#define QS_BIND_QT_DICT_OBJECTS(map, name)                                                 \
    QS_JSON_QT_DICT_OBJECTS(map, name)                                                     \
    QS_XML_QT_DICT_OBJECTS(map,name)                                                       \


/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for STL DICTIONARY TYPES */
#define QS_BIND_STL_DICT(map, name)                                                         \
    QS_JSON_STL_DICT(map, name)                                                             \
    QS_XML_STL_DICT(map, name)                                                              \


/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for STL DICTIONARY TYPES */
#define QS_BIND_STL_DICT_OBJECTS(map, name)                                                 \
    QS_JSON_STL_DICT_OBJECTS(map, name)                                                     \
    QS_XML_STL_DICT_OBJECTS(map,name)                                                       \



/* CREATE AND BIND: */
/* Make primitive field and generate serializable propertyes */
/* For example: QS_FIELD(int, digit), QS_FIELD(bool, flag) */
#define QS_FIELD(type, name)                                                                \
    QS_DECLARE_MEMBER(type, name)                                                           \
    QS_BIND_FIELD(type, name)                                                               \

/* CREATE AND BIND: */
/* Make collection of primitive type objects [collectionType<itemType> name] and generate serializable propertyes for this collection */
/* This collection must be provide method append(T) (it's can be QList, QVector)    */
#define QS_COLLECTION(collectionType, itemType, name)                                       \
    QS_DECLARE_MEMBER(collectionType<itemType>, name)                                       \
    QS_BIND_COLLECTION(itemType, name)                                                      \

/* CREATE AND BIND: */
/* Make custom class object and bind serializable propertyes */
/* This class must be inherited from QSerializer */
#define QS_OBJECT(type,name)                                                                \
    QS_DECLARE_MEMBER(type, name)                                                           \
    QS_BIND_OBJECT(type, name)                                                              \

/* CREATE AND BIND: */
/* Make collection of custom class objects [collectionType<itemType> name] and bind serializable propertyes */
/* This collection must be provide method append(T) (it's can be QList, QVector)    */
#define QS_COLLECTION_OBJECTS(collectionType, itemType, name)                               \
    QS_DECLARE_MEMBER(collectionType<itemType>, name)                                       \
    QS_BIND_COLLECTION_OBJECTS(itemType, name)                                              \


/* CREATE AND BIND: */
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be QT DICTIONARY TYPE */
#define QS_QT_DICT(map, first, second, name)                                                \
    public:                                                                                 \
    typedef map<first,second> dict_##name##_t;                                              \
    dict_##name##_t name = dict_##name##_t();                                               \
    QS_BIND_QT_DICT(dict_##name##_t, name)                                                  \

/* CREATE AND BIND: */
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be QT DICTIONARY TYPE */
#define QS_QT_DICT_OBJECTS(map, first, second, name)                                        \
    public:                                                                                 \
    typedef map<first,second> dict_##name##_t;                                              \
    dict_##name##_t name = dict_##name##_t();                                               \
    QS_BIND_QT_DICT_OBJECTS(dict_##name##_t, name)                                          \

/* CREATE AND BIND: */
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be STL DICTIONARY TYPE */
#define QS_STL_DICT(map, first, second, name)                                               \
    public:                                                                                 \
    typedef map<first,second> dict_##name##_t;                                              \
    dict_##name##_t name = dict_##name##_t();                                               \
    QS_BIND_STL_DICT(dict_##name##_t, name)                                                 \

/* CREATE AND BIND: */
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be STL DICTIONARY TYPE */
#define QS_STL_DICT_OBJECTS(map, first, second, name)                                       \
    public:                                                                                 \
    typedef map<first,second> dict_##name##_t;                                              \
    dict_##name##_t name = dict_##name##_t();                                               \
    QS_BIND_STL_DICT_OBJECTS(dict_##name##_t, name)                                         \
#endif // QSERIALIZER_H

使用起来非常简单且强大:

QS_FIELD Create serializable simple field
QS_COLLECTION Create serializable collection values of primitive types
QS_OBJECT Create serializable inner custom type object
QS_COLLECTION_OBJECTS Create serializable collection of custom type objects
QS_QT_DICT Create serializable dictionary of primitive type values FOR QT DICTIONARY TYPES
QS_QT_DICT_OBJECTS Create serializable dictionary of custom type values FOR QT DICTIONARY TYPES
QS_STL_DICT Create serializable dictionary of primitive type values FOR STL DICTIONARY TYPES
QS_STL_DICT_OBJECTS Create serializable dictionary of custom type values FOR STL DICTIONARY TYPES
posted @ 2023-10-12 11:57  非法关键字  阅读(1518)  评论(0编辑  收藏  举报