[QT] QT realizes the mutual conversion between Json structure and data class through data encapsulation

The most important part of the development project is the data. Here is a data encapsulation method in the QT project. The data encapsulated in the method can be self-monitored, and after the change, it will actively send a message to the outside to notify the change. At the same time, the encapsulated data can be converted to and from the JSON data structure, which is very helpful for data persistence and network requests. .

Basic type encapsulation

In order to improve the reusability of the code, here we encapsulate the general methods and properties with the basic property types to facilitate the calling of the program. The inheritance relationship between basic types is shown in the following figure:

insert image description here

DatabDecoratorIt is the base class of data encapsulation, which contains some common attributes of data content.
StringDecoratorIt is the encapsulation of string type attributes.
IntDecoratorIt is the encapsulation of integer type attributes.
EnumeratorDecoratorIt is the encapsulation of enumeration type data.
DateTimeDecoratorIt is the encapsulation of date type data.

The data base class of the basic type is as follows, which encapsulates the additional auxiliary parameters required by the attribute field, and the method interface for mutual conversion with the Json structure

1、datadecorator.h

#ifndef DATADECORATOR_H
#define DATADECORATOR_H

#include <QJsonObject>
#include <QJsonValue>
#include <QObject>
#include <QScopedPointer>

class Entity;
class DataDecorator : public QObject
{
    
    
    Q_OBJECT
    //导出QML中需要访问属性
    Q_PROPERTY( QString ui_describe READ label CONSTANT )
public:
    /**@brief 构造描述
   * @1 该属性字段属于哪个数据实体
   * @2 该属性字段对应的key值
   * @3 该属性字段对应的描述信息
   */
    DataDecorator(Entity* parent = nullptr, const QString& key = "SomeItemKey", const QString& label = "");
    virtual ~DataDecorator();

    const QString& key() const;   //key值用来查找该属性
    const QString& label() const; //label用来描述该属性字段
    Entity* parentEntity();       //属性字段属于哪个数据实体(比如用户等)

    //Json和数据类之间的相互转化
    virtual QJsonValue jsonValue() const = 0;
    virtual void update(const QJsonObject& jsonObject) = 0;

private:
    //私有的数据类
    class Implementation;
    QScopedPointer<Implementation> implementation;
};
#endif

2、datadecorator.cpp

#include "data-decorator.h"
class DataDecorator::Implementation
{
    
    
public:
    Implementation(Entity* _parent, const QString& _key, const QString& _label)
        : parentEntity(_parent)
        , key(_key)
        , label(_label)
    {
    
    
    }
    Entity* parentEntity{
    
    nullptr};
    QString key;
    QString label;
};

DataDecorator::DataDecorator(Entity* parent, const QString& key, const QString& label)
    : QObject((QObject*)parent)
{
    
    
    implementation.reset(new Implementation(parent, key, label));
}
DataDecorator::~DataDecorator()
{
    
    
}
const QString& DataDecorator::key() const
{
    
    
    return implementation->key;
}

const QString& DataDecorator::label() const
{
    
    
    return implementation->label;
}

Entity* DataDecorator::parentEntity()
{
    
    
    return implementation->parentEntity;
}

The encapsulation class of the basic type is derived from the DataDecorator class, which is implemented as follows:

1. Encapsulation of string type

1、stringdecorator.h

#ifndef STRINGDECORATOR_H
#define STRINGDECORATOR_H

#include <QJsonObject>
#include <QJsonValue>
#include <QObject>
#include <QScopedPointer>
#include <QString>
#include <data/data-decorator.h>

class  StringDecorator : public DataDecorator
{
    
    
    Q_OBJECT
   //QML访问的属性
    Q_PROPERTY( QString ui_value READ value WRITE setValue NOTIFY valueChanged )
public:
    StringDecorator(Entity* parentEntity = nullptr, const QString& key = "SomeItemKey", const QString& label = "", const QString& value = "");
    ~StringDecorator();

    //修改和获取字符串封装类型中的原始数据
    StringDecorator& setValue(const QString& value);
    const QString& value() const;

    QJsonValue jsonValue() const override;
    void update(const QJsonObject& jsonObject) override;

signals:
    //数据发生变化的时候发送的信号
    void valueChanged();

private:
    class Implementation;
    QScopedPointer<Implementation> implementation;
};

#endif

2、stringdecorator.cpp

#include "string-decorator.h"
#include <QVariant>

class StringDecorator::Implementation
{
    
    
public:
    Implementation(StringDecorator* _stringDecorator, const QString& _value)
        : stringDecorator(_stringDecorator)
        , value(_value)
    {
    
    
    }

    StringDecorator* stringDecorator{
    
    nullptr};
    QString value;
};

StringDecorator::StringDecorator(Entity* parentEntity, const QString& key, const QString& label, const QString& value)
    : DataDecorator(parentEntity, key, label)
{
    
    
    implementation.reset(new Implementation(this, value));
}

StringDecorator::~StringDecorator()
{
    
    
}

const QString& StringDecorator::value() const
{
    
    
    return implementation->value;
}

StringDecorator& StringDecorator::setValue(const QString& value)
{
    
    
    if(value != implementation->value) {
    
    
        implementation->value = value;
        emit valueChanged();
    }
    return *this;
}

QJsonValue StringDecorator::jsonValue() const
{
    
    
    return QJsonValue::fromVariant(QVariant(implementation->value));
}

void StringDecorator::update(const QJsonObject& _jsonObject)
{
    
    
    if (_jsonObject.contains(key())) {
    
    
        setValue(_jsonObject.value(key()).toString());
    }
}

2. Encapsulation of integers

1、intdecorator.h

#ifndef INTDECORATOR_H
#define INTDECORATOR_H

#include <QJsonObject>
#include <QJsonValue>
#include <QObject>
#include <QScopedPointer>
#include <data/data-decorator.h>

class  IntDecorator : public DataDecorator
{
    
    
    Q_OBJECT
   // QML访问的接口
    Q_PROPERTY( int ui_value READ value WRITE setValue NOTIFY valueChanged )

public:
    IntDecorator(Entity* parentEntity = nullptr, const QString& key = "SomeItemKey", const QString& label = "", int value = 0);
    ~IntDecorator();
 
    //修改和获取对应的属性值
    IntDecorator& setValue(int value);
    int value() const;

public:
    QJsonValue jsonValue() const override;
    void update(const QJsonObject& jsonObject) override;

signals:
    void valueChanged();

private:
    class Implementation;
    QScopedPointer<Implementation> implementation;
};

#endif

2、intdecorator.cpp

#include "int-decorator.h"
#include <QVariant>

class IntDecorator::Implementation
{
    
    
public:
    Implementation(IntDecorator* intDecorator, int value)
        : intDecorator(intDecorator)
        , value(value)
    {
    
    
    }

    IntDecorator* intDecorator{
    
    nullptr};
    int value;
};

IntDecorator::IntDecorator(Entity* parentEntity, const QString& key, const QString& label, int value)
    : DataDecorator(parentEntity, key, label)
{
    
    
    implementation.reset(new Implementation(this, value));
}

IntDecorator::~IntDecorator()
{
    
    
}

int IntDecorator::value() const
{
    
    
    return implementation->value;
}

IntDecorator& IntDecorator::setValue(int value)
{
    
    
    if(value != implementation->value) {
    
    
        // ...Validation here if required...
        implementation->value = value;
        emit valueChanged();
    }

    return *this;
}

QJsonValue IntDecorator::jsonValue() const
{
    
    
    return QJsonValue::fromVariant(QVariant(implementation->value));
}

void IntDecorator::update(const QJsonObject& jsonObject)
{
    
    
    if (jsonObject.contains(key())) {
    
    
        auto l_value = jsonObject.value(key()).toInt();
        setValue(l_value);
    } else {
    
    
        setValue(0);
    }
}

3. Encapsulate the enumerated type

Different from other basic types, the enumeration type not only contains an integer value, but also contains a mapping table, and there is a corresponding string mapping for each enumeration value, which is used for external display of the enumeration.

1、enumeratordecorator.h

#ifndef ENUMERATORDECORATOR_H
#define ENUMERATORDECORATOR_H
#include <map>
#include <QJsonObject>
#include <QJsonValue>
#include <QObject>
#include <QScopedPointer>
#include <data/data-decorator.h>

class EnumeratorDecorator : public DataDecorator
{
    
    
    Q_OBJECT
    //枚举属性值
    Q_PROPERTY( int ui_value READ value WRITE setValue NOTIFY valueChanged )
    //枚举属性对应的字符串描述
    Q_PROPERTY( QString ui_valueDescription READ valueDescription NOTIFY valueChanged )
public:
    EnumeratorDecorator(Entity* parentEntity = nullptr, const QString& key = "SomeItemKey", const QString& label = "", int value = 0, const std::map<int, QString>& descriptionMapper = std::map<int, QString>());
    ~EnumeratorDecorator();
   
    //获取属性对应的值和描述
    EnumeratorDecorator& setValue(int value);
    int value() const;
    QString valueDescription() const;

    QJsonValue jsonValue() const override;
    void update(const QJsonObject& jsonObject) override;

signals:
    void valueChanged();

private:
    class Implementation;
    QScopedPointer<Implementation> implementation;
};
#endif

2、enumeratordecorator.cpp

#include "enumerator-decorator.h"
#include <QVariant>

class EnumeratorDecorator::Implementation
{
    
    
public:
    Implementation(EnumeratorDecorator* enumeratorDecorator, int value, const std::map<int, QString>& descriptionMapper)
        : enumeratorDecorator(enumeratorDecorator)
        , value(value)
        , descriptionMapper(descriptionMapper)
    {
    
    
    }

    EnumeratorDecorator* enumeratorDecorator{
    
    nullptr};
    int value;
    std::map<int, QString> descriptionMapper;
};

EnumeratorDecorator::EnumeratorDecorator(Entity* parentEntity, const QString& key, const QString& label, int value, const std::map<int, QString>& descriptionMapper)
    : DataDecorator(parentEntity, key, label)
{
    
    
    implementation.reset(new Implementation(this, value, descriptionMapper));
}

EnumeratorDecorator::~EnumeratorDecorator()
{
    
    
}

int EnumeratorDecorator::value() const
{
    
    
    return implementation->value;
}

QString EnumeratorDecorator::valueDescription() const
{
    
    
    if (implementation->descriptionMapper.find(implementation->value) != implementation->descriptionMapper.end()) {
    
    
        return implementation->descriptionMapper.at(implementation->value);
    } else {
    
    
        return {
    
    };
    }
}

EnumeratorDecorator& EnumeratorDecorator::setValue(int value)
{
    
    
    if (value != implementation->value) {
    
    
        // ...Validation here if required...
        implementation->value = value;
        emit valueChanged();
    }

    return *this;
}

QJsonValue EnumeratorDecorator::jsonValue() const
{
    
    
    return QJsonValue::fromVariant(QVariant(implementation->value));
}

void EnumeratorDecorator::update(const QJsonObject& jsonObject)
{
    
    
    if (jsonObject.contains(key())) {
    
    
        auto valueFromJson = jsonObject.value(key()).toInt();
        setValue(valueFromJson);
    } else {
    
    
        setValue(0);
    }
}

4. Encapsulate the date type

Since the date has different display forms, we extend some new interfaces in the date encapsulation type, so that the caller can obtain different display forms of the date.

1、datetimedecorator.h

#ifndef DATETIMEDECORATOR_H
#define DATETIMEDECORATOR_H

#include <QDateTime>
#include <QJsonObject>
#include <QJsonValue>
#include <QObject>
#include <QScopedPointer>
#include <data/data-decorator.h>

class DateTimeDecorator : public DataDecorator
{
    
    
    Q_OBJECT
    //QML中访问各种日期的格式
    Q_PROPERTY( QString ui_iso8601String READ toIso8601String NOTIFY valueChanged )
    Q_PROPERTY( QString ui_prettyDateString READ toPrettyDateString NOTIFY valueChanged )
    Q_PROPERTY( QString ui_prettyTimeString READ toPrettyTimeString NOTIFY valueChanged )
    Q_PROPERTY( QString ui_prettyString READ toPrettyString NOTIFY valueChanged )
    Q_PROPERTY( QDateTime ui_value READ value WRITE setValue NOTIFY valueChanged )

public:
    DateTimeDecorator(Entity* parentEntity = nullptr, const QString& key = "SomeItemKey", const QString& label = "", const QDateTime& value = QDateTime());
    ~DateTimeDecorator();

    const QDateTime& value() const;
    DateTimeDecorator& setValue(const QDateTime& value);

    //获取各种显示格式
    QString toIso8601String() const;
    QString toPrettyDateString() const;
    QString toPrettyTimeString() const;
    QString toPrettyString() const;

    QJsonValue jsonValue() const override;
    void update(const QJsonObject& jsonObject) override;

signals:
    void valueChanged();

private:
    class Implementation;
    QScopedPointer<Implementation> implementation;
};

#endif

2、datetimedecorator.cpp

#include "datetime-decorator.h"
#include <QVariant>

class DateTimeDecorator::Implementation
{
    
    
public:
    Implementation(DateTimeDecorator* dateTimeDecorator, const QDateTime& value)
        : dateTimeDecorator(dateTimeDecorator)
        , value(value)
    {
    
    
    }
    DateTimeDecorator* dateTimeDecorator{
    
    nullptr};
    QDateTime value;
};

DateTimeDecorator::DateTimeDecorator(Entity* parentEntity, const QString& key, const QString& label, const QDateTime& value)
    : DataDecorator(parentEntity, key, label)
{
    
    
    implementation.reset(new Implementation(this, value));
}

DateTimeDecorator::~DateTimeDecorator()
{
    
    
}

const QDateTime& DateTimeDecorator::value() const
{
    
    
    return implementation->value;
}

DateTimeDecorator& DateTimeDecorator::setValue(const QDateTime& value)
{
    
    
    if(value != implementation->value) {
    
    
        // ...Validation here if required...
        implementation->value = value;
        emit valueChanged();
    }

    return *this;
}

QString DateTimeDecorator::toIso8601String() const
{
    
    
    if (implementation->value.isNull()) {
    
    
        return "";
    } else {
    
    
        return implementation->value.toString(Qt::ISODate);
    }
}

QString DateTimeDecorator::toPrettyString() const
{
    
    
    if (implementation->value.isNull()) {
    
    
        return "Not set";
    } else {
    
    
        return implementation->value.toString( "ddd d MMM yyyy @ HH:mm:ss" );
    }
}

QString DateTimeDecorator::toPrettyDateString() const
{
    
    
    if (implementation->value.isNull()) {
    
    
        return "Not set";
    } else {
    
    
        return implementation->value.toString( "d MMM yyyy" );
    }
}

QString DateTimeDecorator::toPrettyTimeString() const
{
    
    
    if (implementation->value.isNull()) {
    
    
        return "Not set";
    } else {
    
    
        return implementation->value.toString( "hh:mm ap" );
    }
}

QJsonValue DateTimeDecorator::jsonValue() const
{
    
    
    return QJsonValue::fromVariant(QVariant(implementation->value.toString(Qt::ISODate)));
}

void DateTimeDecorator::update(const QJsonObject& jsonObject)
{
    
    
    if (jsonObject.contains(key())) {
    
    
        auto valueAsString = jsonObject.value(key()).toString();
        auto valueAsDate = QDateTime::fromString(valueAsString, Qt::ISODate);  // yyyy-MM-ddTHH:mm:ss
        setValue(valueAsDate);
    } else {
    
    
        setValue(QDateTime());
    }
}

Data information class encapsulation

After the basic types are encapsulated, the data information class can be further encapsulated. The data information class is an organic data collection composed of many basic types, and there is a logical relationship between the basic data. For example, the consumer information class includes: the name of the string type, the age of the integer type, the address of the string type, the gender of the string type, and the like. The correspondence between data information classes and basic types can be one-to-one or one-to-many. For example, a person has only one name but may have multiple addresses. This needs to be taken into account when encapsulating. The relationship between the encapsulation classes of the data information class is shown in the figure below:
insert image description here
Entity, the base class of the data entity, encapsulates entities that require basic properties and methods, and a data entity contains multiple basic types of properties. At the same time, a data entity may contain multiple other data entities, for example, a user may have multiple accounts with multiple vehicles and so on.

1. Encapsulation of data information body

Entity contains the basic properties and methods of data information entities as follows:

1、entity.h

#ifndef ENTITY_H
#define ENTITY_H

#include <map>
#include <QObject>
#include <QScopedPointer>
#include <data/data-decorator.h>
#include <data/entity-collection.h>

class Entity : public QObject
{
    
    
    Q_OBJECT

public:
    Entity(QObject* parent = nullptr, const QString& key = "SomeEntityKey");
    Entity(QObject* parent, const QString& key, const QJsonObject& jsonObject);
    virtual ~Entity();

public:
    //数据信息体对应的key值
    const QString& key() const;

    //数据信息类和Json结构之间的相互转换
    void update(const QJsonObject& jsonObject);
    QJsonObject toJson() const;

signals:
    //信息体集合发生变化
    void childCollectionsChanged(const QString& collectionKey);
    //包含的信息体发生变化
    void childEntitiesChanged();
    //包含的属性发生变化
    void dataDecoratorsChanged();

protected:
    //添加包含的信信息体
    Entity* addChild(Entity* entity, const QString& key);
    //添加包含的信息实体列表
    EntityCollectionBase* addChildCollection(EntityCollectionBase* entityCollection);
    //添加子属性
    DataDecorator* addDataItem(DataDecorator* dataDecorator);
protected:
    class Implementation;
    QScopedPointer<Implementation> implementation;
};

#endif

2、entity.cpp

#include "entity.h"
#include <QJsonArray>

class Entity::Implementation
{
    
    
public:
    Implementation(Entity* _entity, const QString& _key)
        : entity(_entity)
        , key(_key)
    {
    
    
    }
    Entity* entity{
    
    nullptr};
    QString key;
    std::map<QString, EntityCollectionBase*> childCollections;
    std::map<QString, Entity*> childEntities;
    std::map<QString, DataDecorator*> dataDecorators;
};

Entity::Entity(QObject* parent, const QString& key)
    : QObject(parent)
{
    
    
    implementation.reset(new Implementation(this, key));
}

Entity::Entity(QObject* parent, const QString& key, const QJsonObject& jsonObject)
    : Entity(parent, key)
{
    
    
    update(jsonObject);
}

Entity::~Entity()
{
    
    
}
const QString& Entity::key() const
{
    
    
    return implementation->key;
}

Entity* Entity::addChild(Entity* entity, const QString& key)
{
    
    
    if(implementation->childEntities.find(key) == std::end(implementation->childEntities)) {
    
    
        implementation->childEntities[key] = entity;
        emit childEntitiesChanged();
    }

    return entity;
}

EntityCollectionBase* Entity::addChildCollection(EntityCollectionBase* entityCollection)
{
    
    
    if(implementation->childCollections.find(entityCollection->getKey()) == std::end(implementation->childCollections)) {
    
    
        implementation->childCollections[entityCollection->getKey()] = entityCollection;
        emit childCollectionsChanged(entityCollection->getKey());
    }

    return entityCollection;
}

DataDecorator* Entity::addDataItem(DataDecorator* dataDecorator)
{
    
    
    if(implementation->dataDecorators.find(dataDecorator->key()) == std::end(implementation->dataDecorators)) {
    
    
        implementation->dataDecorators[dataDecorator->key()] = dataDecorator;
        emit dataDecoratorsChanged();
    }
    return dataDecorator;
}

void Entity::update(const QJsonObject& jsonObject)
{
    
    
    // Update data decorators
    for (std::pair<QString, DataDecorator*> dataDecoratorPair : implementation->dataDecorators) {
    
    
        dataDecoratorPair.second->update(jsonObject);
    }

    // Update child entities
    for (std::pair<QString, Entity*> childEntityPair : implementation->childEntities) {
    
    
        childEntityPair.second->update(jsonObject.value(childEntityPair.first).toObject());
    }

    // Update child collections
    for (std::pair<QString, EntityCollectionBase*> childCollectionPair : implementation->childCollections) {
    
    
        childCollectionPair.second->update(jsonObject.value(childCollectionPair.first).toArray());
    }
}

QJsonObject Entity::toJson() const
{
    
    
    QJsonObject returnValue;

    // Add data decorators
    for (std::pair<QString, DataDecorator*> dataDecoratorPair : implementation->dataDecorators) {
    
    
        returnValue.insert( dataDecoratorPair.first, dataDecoratorPair.second->jsonValue() );
    }

    // Add child entities
    for (std::pair<QString, Entity*> childEntityPair : implementation->childEntities) {
    
    
        returnValue.insert( childEntityPair.first, childEntityPair.second->toJson() );
    }

    // Add child collections
    for (std::pair<QString, EntityCollectionBase*> childCollectionPair : implementation->childCollections) {
    
    
        QJsonArray entityArray;
            for (Entity* entity : childCollectionPair.second->baseEntities()) {
    
    
            entityArray.append( entity->toJson() );
        }
        returnValue.insert( childCollectionPair.first, entityArray );
    }

    return returnValue;
}

2. Data information collection encapsulation

The data information collection is to encapsulate the list of data bodies. For example, a user contains an address list, and the address list is a collection of address information. Through template operations, we can realize the serialization and deserialization of various information entities. The realization of information collection is as follows:

#ifndef ENTITYCOLLECTION_H
#define ENTITYCOLLECTION_H

#include <QJsonArray>
#include <QJsonValue>
#include <QObject>

class Entity;
//基类声明集合变化的信号
class EntityCollectionObject : public QObject
{
    
    
    Q_OBJECT

public:
    EntityCollectionObject(QObject* _parent = nullptr) : QObject(_parent) {
    
    }
    virtual ~EntityCollectionObject() {
    
    }

signals:
    void collectionChanged();
};

//数据信息集合的基类
class EntityCollectionBase : public EntityCollectionObject
{
    
    
public:
    EntityCollectionBase(QObject* parent = nullptr, const QString& key = "SomeCollectionKey")
        : EntityCollectionObject(parent)
        , key(key)
    {
    
    }

    virtual ~EntityCollectionBase()
    {
    
    }

    QString getKey() const
    {
    
    
        return key;
    }

    virtual void clear() = 0;
    //json数组转成数据实体
    virtual void update(const QJsonArray& json) = 0;
    virtual std::vector<Entity*> baseEntities() = 0;

    template <class T>
    QList<T*>& derivedEntities();

    template <class T>
    T* addEntity(T* entity);

private:
    QString key;
};

//数据信息基类
template <typename T>
class EntityCollection : public EntityCollectionBase
{
    
    
public:
    EntityCollection(QObject* parent = nullptr, const QString& key = "SomeCollectionKey")
        : EntityCollectionBase(parent, key)
    {
    
    }

    ~EntityCollection()
    {
    
    }

    //清空列表
    void clear() override
    {
    
    
        for(auto entity : collection) {
    
    
            entity->deleteLater();
        }
        collection.clear();
    }
    //更新列表
    void update(const QJsonArray& jsonArray) override
    {
    
    
        clear();
        for(const QJsonValue& jsonValue : jsonArray) {
    
    
            addEntity(new T(this, jsonValue.toObject()));
        }
    }
   
    //获取列表
    std::vector<Entity*> baseEntities() override
    {
    
    
        std::vector<Entity*> returnValue;
        for(T* entity : collection) {
    
    
            returnValue.push_back(entity);
        }
        return returnValue;
    }
   
    //获取列表的引用
    QList<T*>& derivedEntities()
    {
    
    
        return collection;
    }
   
    //添加新的信息实体
    T* addEntity(T* entity)
    {
    
    
        if(!collection.contains(entity)) {
    
    
            collection.append(entity);
            EntityCollectionObject::collectionChanged();
        }
        return entity;
    }
private:
    QList<T*> collection;
};

template <class T>
QList<T*>& EntityCollectionBase::derivedEntities()
{
    
    
    return dynamic_cast<const EntityCollection<T>&>(*this).derivedEntities();
}

template <class T>
T* EntityCollectionBase::addEntity(T* entity)
{
    
    
    return dynamic_cast<const EntityCollection<T>&>(*this).addEntity(entity);
}

#endif

Use encapsulated data structures

1. Define new business data types

After the encapsulation is complete, we can use our encapsulated type in the project. Here we take a consumer Customer as an example to illustrate how to use the basic type of encapsulation. The Customer consumer contains two basic attributes: the name of the string type, the age of the integer type, and a list of addresses. The address contains information such as the postal code of the city street, and the implementation of the address is as follows:
1. address.h

#ifndef ADDRESS_H
#define ADDRESS_H

#include <QObject>
#include <data/string-decorator.h>
#include <data/entity.h>

class Address : public Entity
{
    
    
    Q_OBJECT
    Q_PROPERTY(StringDecorator* ui_building MEMBER building CONSTANT)
    Q_PROPERTY(StringDecorator* ui_street MEMBER street CONSTANT)
    Q_PROPERTY(StringDecorator* ui_city MEMBER city CONSTANT)
    Q_PROPERTY(StringDecorator* ui_postcode MEMBER postcode CONSTANT)
    Q_PROPERTY(QString ui_fullAddress READ fullAddress CONSTANT)

public:
    explicit Address(QObject* parent = nullptr);
    Address(QObject* parent, const QJsonObject& json);

    StringDecorator* building{
    
    nullptr}; //建筑
    StringDecorator* street{
    
    nullptr};   //街道
    StringDecorator* city{
    
    nullptr};     //城市
    StringDecorator* postcode{
    
    nullptr}; //邮政编码

    QString fullAddress() const;
};

#endif

2、address.cpp

#include "address.h"

Address::Address(QObject* parent)
        : Entity(parent, "address")
{
    
    
    building = static_cast<StringDecorator*>(addDataItem(new StringDecorator(this, "building", "Building")));
    street = static_cast<StringDecorator*>(addDataItem(new StringDecorator(this, "street", "Street")));
    city = static_cast<StringDecorator*>(addDataItem(new StringDecorator(this, "city", "City")));
    postcode = static_cast<StringDecorator*>(addDataItem(new StringDecorator(this, "postcode", "Post Code")));
}

Address::Address(QObject* parent, const QJsonObject& json)
        : Address(parent)
{
    
    
    update(json);
}

QString Address::fullAddress() const
{
    
    
    return building->value() + " " + street->value() + "\n" + city->value() + "\n" + postcode->value();
}
In addition to the basic attributes, the consumer information class also contains a set of address information. The corresponding implementation is as follows:

1、customer.h

#ifndef Customer_H
#define Customer_H

#include <QObject>
#include <QtQml/QQmlListProperty>
#include <data/string-decorator.h>
#include <data/int-decorator.h>
#include <data/entity.h>
#include <data/entity-collection.h>
#include <data/address.h>

class Customer : public Entity
{
    
    
    Q_OBJECT
    //QML访问的信息接口
    //年龄信息
    Q_PROPERTY( IntDecorator* ui_age MEMBER age CONSTANT )
    //姓名信息
    Q_PROPERTY( StringDecorator* ui_name MEMBER name CONSTANT )
    Q_PROPERTY( Address* ui_mainAddress MEMBER defaultAddr CONSTANT ) //默认地址
    //地址列表
    Q_PROPERTY( QQmlListProperty<Address> ui_address READ ui_address NOTIFY addresssListChanged)

public:
    explicit Customer(QObject* parent = nullptr);
    Customer(QObject* parent, const QJsonObject& json);

    IntDecorator* age{
    
    nullptr};       //年龄
    StringDecorator* name{
    
    nullptr};   //姓名
    Address* defaultAddr{
    
    nullptr};    //默认地址
    //地址列表
    EntityCollection<Address>* addresses{
    
    nullptr};
    //qml访问的地址列表
    QQmlListProperty<Address> ui_address();

signals:
    void addresssListChanged();
};

#endif

2、customer.cpp

#include "customer.h"

Customer::Customer(QObject* parent)
    : Entity(parent, "Customer")
{
    
    
    age = static_cast<IntDecorator*>(addDataItem(new IntDecorator(this, "age", "年龄")));
    name = static_cast<StringDecorator*>(addDataItem(new StringDecorator(this, "name", "姓名")));
    defaultAddr = static_cast<Address*>(addChild(new Address(this), "defaultAddress"));
    addresses = static_cast<EntityCollection<Address>*>(addChildCollection(new EntityCollection<Address>(this, "address")));
}

Customer::Customer(QObject* parent, const QJsonObject& json)
    : Customer(parent)
{
    
    
    update(json);
}

QQmlListProperty<Address> Customer::ui_address()
{
    
    
    return QQmlListProperty<Address>(this, addresses->derivedEntities());
}

2. Use new data types

By using the new encapsulation type, we can realize the mutual conversion between the Json structure and the data class, and the usage method is as follows:

#include "widget.h"
#include <QApplication>
#include <data/customer.h>
#include <data/address.h>

int main(int argc, char *argv[])
{
    
    
    QApplication a(argc, argv);
    //将json结构转换成一个数据类
    QJsonObject customer_json;
    Customer* customer = new Customer(NULL,customer_json);
    //名字变化处理
    QObject::connect(customer->name,&StringDecorator::valueChanged,[&](){
    
    });
    //年龄变化的处理
    QObject::connect(customer->age,&IntDecorator::valueChanged,[&](){
    
    });
    //默认地址变化的处理
    QObject::connect(customer->defaultAddr,&Address::dataDecoratorsChanged,[&](){
    
    });

    //修改数据
    customer->name->setValue("小明");
    customer->age->setValue(23);
    customer->defaultAddr->city->setValue("北京市");
    customer->defaultAddr->street->setValue("光明街道");
    customer->defaultAddr->building->setValue("6号楼");
    customer->defaultAddr->postcode->setValue("56788");

    //添加新地址
    Address* new_addr = new Address();
    new_addr->city->setValue("西河市");
    new_addr->street->setValue("细节街道");
    new_addr->building->setValue("6号楼");
    new_addr->postcode->setValue("56789");
    customer->addresses->addEntity(new_addr);

    //将数据类型转换成json数据结构
    QJsonObject json_object = customer->toJson();
    return a.exec();
}

Through this encapsulation, we can realize the mutual conversion between data classes and Json data structures, and all attributes have description information, which is very helpful for our reuse and expansion.

Guess you like

Origin blog.csdn.net/Cappuccino_jay/article/details/126604433