简单实现QT对象的[json]序列化与反序列化

简单实现QT对象的[json]序列化与反序列化

  • 简介
  • 应用场景
  • qt元对象系统
  • 思路
  • 实现
  • 使用方式
  • 题外话

简介

众所周知json作为一种轻量级的数据交换格式,在开发中被广泛应用。因此如何方便的将对象数据转为json格式和从json格式中加载数据到对象中就变得尤为重要。
在python类动态语言中,我们可以很方便的使用json.dumps()和json.load()完成json数据的生成和加载。但是在QT中就不能非常方便的转换。
因此本文将使用QT中的元对象系统实现简单的json数据转换和加载

应用场景

假如现在需要开发一个系统,采用c/s架构,使用json格式作为前后端的数据交互协议,那么针对每个接口,所需要交互的数据都需要转换为json或者从json中获取。
举个栗子:
现有个需要,需要根据用户id获取用户的详细信息,由客户端发起请求,服务端返回数据

//客户端
//1.定义数据结构体
struct requestUserInfo
{
	int id = -1;
};
struct responseUserInfo
{
	QString name;
	int age;
	//....
};
//2.定义数据
requestUserInfo _data;
_data.id = 1;
//3.转换为json数据,转换过程就不展示了
QByteArray _requestData = "xxx";
auto _result = network.sendData(_requestData);
//4.将结果转换为responseUserInfo,转换过程就不详细描述了
responseUserInfo _responseData;
//5.显示详细信息
//.....

在这个过程中,可以很清楚的看到,第3步和第4步的转换和解析过程将会耗费大量的事件书写很多冗余的代码,如果能够很方便的将结构体(对象)转换为json 和从json转换为结构体。代码的书写将会变得简洁而高效。

qt元对象系统

在此之前我们需要先了解一下qt的元对象系统是什么,ai回答如下:

  • 信号和槽机制:Qt的元对象系统支持信号和槽之间的通信,这是Qt中对象间通信的主要方式。信号是由对象发出的通知,告知发生了某个事件,而槽是响应这些信号的函数。这种机制替代了传统的回调函数,使得对象间的协作更加简单和直观
  • 运行时类型信息:元对象系统提供了运行时类型信息(RTTI),允许程序在运行时查询对象的类型。这包括获取类的名称、父类的名称、类中信号和槽的数量和名称等
  • 动态属性系统:Qt的元对象系统支持动态属性,这意味着可以在运行时添加、修改或删除对象的属性。这些属性可以用于Qt Designer工具中,也可以在QML中使用
  • 继承和多态:Qt的元对象系统支持面向对象编程中的继承和多态特性。通过使用Q_OBJECT宏和元对象编译器(MOC),Qt能够为继承自QObject的类生成额外的代码,以支持信号和槽、属性等元对象特性
  • 元对象编译器(MOC):为了使用元对象系统,需要在类定义中包含Q_OBJECT宏,然后使用MOC编译器生成额外的代码来支持元对象特性。MOC是Qt构建过程中自动调用的,它解析C++头文件,并为包含Q_OBJECT宏的类生成运行时所需的代码
  • 对象树和所有权:Qt使用对象树来组织和管理所有的QObject及其子类的对象。当一个对象被创建并指定另一个对象为其父对象时,它会被添加到父对象的孩子列表中。当父对象被销毁时,子对象也会自动被销毁,这有助于简化内存管理并减少内存泄漏的风险
  • 自定义属性:Qt提供了基于元对象系统的自定义属性机制,允许开发者定义自己的属性,并在Qt Designer和QML中使用这些属性。这些属性可以通过Q_PROPERTY宏来声明,并可以设置为动态属性

总之,继承至QObject的子类,在运行时可以通过qt的元对象系统获取对象的名称和属性信息,并且可以自定义信息,其他的特性不在本文讨论的范围中,如果大家感兴趣,后续可以专门写篇文章介绍。

思路

上面提到Qt的元对象系统可以在运行时获取对象的属性,并且支持自定义属性,所以我们就需要借助元对象的这两个属性来完成对象的序列化和反序列化。
在利用qt的元对象特性,利用QMetaObject类获取属性名称和属性值,在转换为json格式,同理从json格式加载时,也可以利用QMetaObject类向属性中写入数据。

实现

通过上述的原理,我实现了简单的转换和加载函数,都包含在一个头文件中,使用时也只需要包含头文件即可,还是很方便。
jsonHelper.h

#include <QObject>
#include <QJsonObject>
#include <QMetaObject>
#include <QMetaProperty>
#include <QMetaMethod>
#include <QJsonValue>

#define READ_WRITE_VALUE(_type,val) 		\
_type get_##val() const {return val;}			\
void set_##val(const _type& _t){val = _t;}

#define READ_WRITE_VALUE_TEMPLATE(val) \
auto get_##val() const -> decltype(val) { return val; } \
void set_##val(const decltype(val)& _t) { val = _t; }

#define READ_FUNC_NAME(_name) 	get_##_name
#define WRITE_FUNC_NAME(_name) 	set_##_name

#define READ_OBJECT(_name)	Q_INVOKABLE QJsonObject read_##_name(){return objDump(&_name);}

#define WRITE_OBJECT(_name)	Q_INVOKABLE void set_##_name(const QJsonObject& _jsonObj){ objLoad(_jsonObj,&_name);}

inline QJsonObject objDump(QObject* _obj)
{
    QJsonObject _jsonObj;

    auto _meatObj = _obj->metaObject();

    for(int i = _meatObj->propertyOffset(); i < _meatObj->propertyCount(); i++)
    {
        auto _property = _meatObj->property(i);
        auto _type = _property.type();
        if(_type < QVariant::UserType)
        {
            _jsonObj[_property.name()] = _property.read(_obj).toJsonValue();
        }
    }

    for(int i = _meatObj->methodOffset(); i < _meatObj->methodCount(); i++)
    {
        auto _meth = _meatObj->method(i);
        if(_meth.returnType() != QMetaType::QJsonObject)
        {
            continue;
        }

        QJsonObject _tempObj;
        _meth.invoke(_obj,Qt::AutoConnection,Q_RETURN_ARG(QJsonObject,_tempObj));

        QString _key = _meth.name();
        _key = _key.remove("read_");
        _jsonObj[_key] = _tempObj;
    }

    return _jsonObj;
}

inline void objLoad(const QJsonObject& jsonObj,QObject* _obj)
{
    auto _meatObj = _obj->metaObject();

    for(int i = _meatObj->propertyOffset(); i < _meatObj->propertyCount(); i++)
    {
        auto _property = _meatObj->property(i);
        QJsonValue _val = jsonObj[_property.name()];
        _property.write(_obj,_val.toVariant());
    }

    for(int i = _meatObj->methodOffset(); i < _meatObj->methodCount(); i++)
    {
        auto _meth = _meatObj->method(i);
        if(_meth.returnType() != QMetaType::Void)
        {
            continue;
        }

        QString _key = _meth.name();
        _key = _key.remove("set_");
        QJsonObject tempObj = jsonObj[_key].toObject();
        _meth.invoke(_obj,Q_ARG(QJsonObject,tempObj));

    }
}

使用方式

  • 定义数据结构对象
#include "JsonHelper.h"

class testStruct : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int a READ READ_FUNC_NAME(a) WRITE WRITE_FUNC_NAME(a) CONSTANT)
public:
    testStruct &operator=(const testStruct& other)
    {
        if(this != &other)
        {
            this->a = other.a;
        }

        return *this;
    }
    int a;

protected:
    READ_WRITE_VALUE(int,a)
};

class myStruct : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int id READ READ_FUNC_NAME(id) WRITE WRITE_FUNC_NAME(id) CONSTANT)
    Q_PROPERTY(QString name READ READ_FUNC_NAME(name) WRITE WRITE_FUNC_NAME(name) CONSTANT)
    Q_PROPERTY(QStringList nameList READ READ_FUNC_NAME(nameList) WRITE WRITE_FUNC_NAME(nameList) CONSTANT)
public:
    int id;
    QString name;
    QStringList nameList;
    testStruct _s1;

protected:
    READ_WRITE_VALUE(int,id)
    READ_WRITE_VALUE(QString,name)
    READ_WRITE_VALUE(QStringList,nameList)
    READ_OBJECT(_s1)
    WRITE_OBJECT(_s1)

};
  • 类型转换与加载
void int main()
{
	testStruct _ss;
    _ss.a = 12;

    myStruct _t;
    _t.id = 1;
    _t.name = "小明";
    _t.nameList = QStringList({"zhangsan","lisi"});
    _t._s1 = _ss;

    auto _obj = objDump(&_t);
    qDebug() << _obj;

    myStruct _t1;
    objLoad(_obj,&_t1);

    qDebug() << _t1.id << _t1.name << _t1.nameList << _t1._s1.a;

	return 0;
}

题外话

可以发现定义数据结构时还是比较繁琐的,如果有更方便的方式来定义数据结构,在实际场景中可用性将会变的更好。
因此,我实现了一个简单的代码自动生成工具,可以将struct结构体转换为满足上述条件的数据对象。如下所示。
在这里插入图片描述
这样就可以不用费劲的编写数据对象结构了。
工具和代码都已经上传GitHub了,感兴趣可以下载使用
GitHub链接
也提供了打包好的生成工具,可直接下载使用:
生成工具链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/920847.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java开发经验——开发常用工具类

摘要 本文介绍了Java开发中常用的工具类&#xff0c;包括Apache Commons Collections的SetUtils、Google Guava的Sets、Apache Commons Lang的ArrayUtils等&#xff0c;以及它们在集合操作、数组操作、字符串处理、JSON处理等方面的应用。文章还涉及了Optional类、Money工具类…

esp32c3开发板通过micropython的mqtt库连MQTT物联网消息服务器

MQTT介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的消息协议&#xff0c;旨在设备之间进行通信&#xff0c;尤其是在网络条件较差的情况下。MQTT v3.1.1 和 MQTT v5 是该协议的两个主要版本。 MQTT v3.1.1&#xff1a; 优点&#xff…

【IDE】使用指南

定期更新实用技能&#xff0c;建议关注收藏点赞。 友情链接&#xff1a; 点击跳转常见代码编辑器的报错解决方案 目录 常用快捷键pycharm右下角边栏脚本头安装IDE的插件git配置TODO 代码编辑器里有许多小技巧&#xff0c;便于办公。本篇主要以pycharm,vscode等主流常用IDE为…

OpenGL入门009——漫反射在片段着色器中的应用

本节将在片段着色器中应用漫反射 文章目录 一些概念漫反射 实战简介dependenciesshadervsshader.fs utilsCube.cpp main.cppCMakeLists.txt最终效果 一些概念 漫反射 概述&#xff1a; 描述的是粗糙表面对光的反射&#xff0c;反射的光线相关各个方向均匀分布&#xff0c;与视…

删库跑路,启动!

起因&#xff1a;这是一个悲伤的故事&#xff0c;在抓logcat时 device待机自动回根目录了&#xff0c;而题主对当前路径的印象还停留在文件夹下&#xff0c;不小心在根目录执行了rm -rf * … 所以&#xff0c;这是个悲伤的故事&#xff0c;东西全没了…device也黑屏了&#xff…

Ubuntu下的Eigen库的安装及基本使用教程

一、Eigen库介绍 简介 Eigen [1]目前最新的版本是3.4&#xff0c;除了C标准库以外&#xff0c;不需要任何其他的依赖包。Eigen使用的CMake建立配置文件和单元测试&#xff0c;并自动安装。如果使用Eigen库&#xff0c;只需包特定模块的的头文件即可。 基本功能 Eigen适用范…

OpenCV与AI深度学习|16个含源码和数据集的计算机视觉实战项目(建议收藏!)

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;分享&#xff5c;16个含源码和数据集的计算机视觉实战项目 本文将分享16个含源码和数据集的计算机视觉实战项目。具体包括&#xff1a; 1. 人…

MySQL win安装 和 pymysql使用示例

目录 一、MySQL安装 下载压缩包&#xff1a; 编写配置文件&#xff1a; 配置环境变量&#xff1a; 初始化服务和账户 关闭mysql开机自启&#xff08;可选&#xff09; 建议找一个数据库可视化软件 二、使用pymysql操作数据库 安装pymysql 示例代码 报错处理 一、My…

springboot基于微信小程序的停车场管理系统

摘 要 停车场管理系统是一种基于移动端的应用程序&#xff0c;旨在方便车主停车的事务办理。该小程序提供了便捷的停车和功能&#xff0c;使车主能够快速完成各项必要的手续和信息填写。旨在提供一种便捷、高效的预约停车方式&#xff0c;减少停车手续的时间和精力成本。通过该…

js:数组转换为字符串

1、使用join 通过join&#xff0c;将数组拼接&#xff0c;使用&#xff0c;进行分割 let array [a, b, c] let str array.join(,); console.log(str) 2、使用toString() const array [a, b, c] const string array.toString() console.log(string) 3、使用扩展运算符和…

npm上传自己封装的插件(vue+vite)

一、npm账号及发包删包等命令 若没有账号&#xff0c;可在npm官网&#xff1a;https://www.npmjs.com/login 进行注册。 在当前项目根目录下打开终端命令窗口&#xff0c;常见命令如下&#xff1a; 1、登录命令&#xff1a;npm login&#xff08;不用每次都重新登录&#xff0…

路由缓存后跳转到新路由时,上一路由中的tip信息框不销毁问题解决

上一路由tip信息框不销毁问题解决 路由缓存篇问题描述及截图解决思路关键代码 路由缓存篇 传送门 问题描述及截图 路由缓存后跳转新路由时&#xff0c;上一个路由的tip信息框没销毁。 解决思路 在全局路由守卫中获取DOM元素&#xff0c;通过css去控制 关键代码 修改文…

uni-app 界面TabBar中间大图标设置的两种方法

一、前言 最近写基于uni-app 写app项目的时候&#xff0c;底部导航栏 中间有一个固定的大图标&#xff0c;并且没有激活状态。这里记录下实现方案。效果如下&#xff08;党组织这个图标&#xff09;&#xff1a; 方法一&#xff1a;midButton的使用 官方文档&#xff1a;ta…

Apple Vision Pro开发003-PolySpatial2.0新建项目

unity6.0下载链接:Unity 实时开发平台 | 3D、2D、VR 和 AR 引擎 一、新建项目 二、导入开发包 com.unity.polyspatial.visionos 输入版本号 2.0.4 com.unity.polyspatial&#xff08;单独导入&#xff09;&#xff0c;或者直接安装 三、对应设置 其他的操作与之前的版本相同…

xiaolin coding 图解网络笔记——基础篇

基础篇 Linux 系统是如何收发网络包的&#xff1f; 网络模型 为了使多种设备能通过网络相互通信&#xff0c;和为了解决不同设备在网络互连中的兼容性问题&#xff0c;国际标准化组织制定了开放式系统互连通信参考模型&#xff08;Open System Interconnection Reference Mo…

【vba源码】导入excel批注信息

Hi&#xff0c;大家好呀&#xff01; 又到了一周一分享的时间&#xff0c;上周繁忙的我都没有给大家直播&#xff0c;视频也没更新&#xff0c;那这周大家放心&#xff0c;都会给大家更新&#xff0c;今天我们来讲点啥呢&#xff1f;每周找优质的内容给大家更新是我最最痛苦的…

跨平台WPF框架Avalonia教程 十三

AutoCompleteBox 自动补全输入框 自动补全输入框提供了一个供用户输入的文本框和一个包含可能匹配项的下拉列表。下拉列表会在用户开始输入时显示&#xff0c;并且每输入一个字符&#xff0c;匹配项都会更新。用户可以从下拉列表中选择匹配项。 文本与可能项匹配的方式是可配…

MATLAB实现GARCH(广义自回归条件异方差)模型计算VaR(Value at Risk)

MATLAB实现GARCH(广义自回归条件异方差)模型计算VaR(Value at Risk) 1.计算模型介绍 使用GARCH&#xff08;广义自回归条件异方差&#xff09;模型计算VaR&#xff08;风险价值&#xff09;时&#xff0c;方差法是一个常用的方法。GARCH模型能够捕捉到金融时间序列数据中的波…

力扣 LeetCode 513. 找树左下角的值(Day8:二叉树)

解题思路&#xff1a; 方法一&#xff1a;递归法&#xff08;方法二更好理解&#xff0c;个人更习惯方法二&#xff09; 前中后序均可&#xff0c;实际上没有中的处理 中左右&#xff0c;左中右&#xff0c;左右中&#xff0c;实际上都是左在前&#xff0c;所以遇到的第一个…

Nuget For Unity插件介绍

NuGet for Unity&#xff1a;提升 Unity 开发效率的利器 NuGet 是 .NET 开发生态中不可或缺的包管理工具,你可以将其理解为Unity的Assets Store或者UPM,里面有很多库可以帮助我们提高开发效率。当你想使用一个库,恰好这个库没什么依赖(比如newtonjson),那么下载包并找到Dll直接…