首页 > 编程语言 >C++中使用Qt实现JSON序列化与反序列化

C++中使用Qt实现JSON序列化与反序列化

时间:2024-09-29 15:14:59浏览次数:7  
标签:QJsonValue const Qt json value JSON C++ 序列化 Serializer

// File: JsonSerializer
// Author: [email protected]
// Creation: 2024/09/29
#ifndef JSON_SERIALIZER_H
#define JSON_SERIALIZER_H

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <type_traits>

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

/* CONTAINER TYPE */
#include <QVector>
#include <QList>
#include <QMap>
#include <QHash>
#include <vector>
#include <map>

// Forward declarations
template<typename T, typename Enable = void>
struct Serializer;

// Primary template for QJsonValue conversion
template<typename T>
struct ToJsonValue {
	static QJsonValue convert(const T& value) {
		return QJsonValue::fromVariant(QVariant::fromValue(value));
	}
};

// Specialization for QJsonValue
template<>
struct ToJsonValue<QJsonValue> {
	static QJsonValue convert(const QJsonValue& value) {
		return value;
	}
};

// Serializer for primitive types
template<typename T>
struct Serializer<T, typename std::enable_if<std::is_arithmetic<T>::value || std::is_same<T, QString>::value>::type> {
	static QJsonValue toJson(const T& value) {
		return ToJsonValue<T>::convert(value);
	}

	static T fromJson(const QJsonValue& json) {
		return json.toVariant().value<T>();
	}
};

// Serializer for QList and QVector
template<template<typename> class Container, typename T>
struct Serializer<Container<T>, typename std::enable_if<std::is_same<Container<T>, QList<T>>::value || std::is_same<Container<T>, QVector<T>>::value>::type> {
	static QJsonValue toJson(const Container<T>& container) {
		QJsonArray array;
		for (const auto& item : container) {
			array.append(Serializer<T>::toJson(item));
		}
		return array;
	}

	static Container<T> fromJson(const QJsonValue& json) {
		Container<T> result;
		if (json.isArray()) {
			QJsonArray array = json.toArray();
			for (const auto& item : array) {
				result.append(Serializer<T>::fromJson(item));
			}
		}
		return result;
	}
};

// Serializer for std::vector
template<typename T>
struct Serializer<std::vector<T>> {
	static QJsonValue toJson(const std::vector<T>& container) {
		QJsonArray array;
		for (const auto& item : container) {
			array.append(Serializer<T>::toJson(item));
		}
		return array;
	}

	static std::vector<T> fromJson(const QJsonValue& json) {
		std::vector<T> result;
		if (json.isArray()) {
			QJsonArray array = json.toArray();
			for (const auto& item : array) {
				result.push_back(Serializer<T>::fromJson(item));
			}
		}
		return result;
	}
};

// Serializer for QMap and QHash
template<template<typename, typename> class Map, typename K, typename V>
struct Serializer<Map<K, V>, typename std::enable_if<std::is_same<Map<K, V>, QMap<K, V>>::value || std::is_same<Map<K, V>, QHash<K, V>>::value>::type> {
	static QJsonValue toJson(const Map<K, V>& map) {
		QJsonObject obj;
		for (auto it = map.begin(); it != map.end(); ++it) {
			obj.insert(ToJsonValue<K>::convert(it.key()).toString(), Serializer<V>::toJson(it.value()));
		}
		return obj;
	}

	static Map<K, V> fromJson(const QJsonValue& json) {
		Map<K, V> result;
		if (json.isObject()) {
			QJsonObject obj = json.toObject();
			for (auto it = obj.begin(); it != obj.end(); ++it) {
				result.insert(ToJsonValue<K>::convert(it.key()).toVariant().value<K>(),
					Serializer<V>::fromJson(it.value()));
			}
		}
		return result;
	}
};

// Serializer for std::map
template<typename K, typename V>
struct Serializer<std::map<K, V>> {
	static QJsonValue toJson(const std::map<K, V>& map) {
		QJsonObject jsonObject;
		for (const auto& pair : map) {
			QString key = ToJsonValue<K>::convert(pair.first).toString();
			QJsonValue value = Serializer<V>::toJson(pair.second);
			jsonObject.insert(key, value);
		}
		return jsonObject;
	}

	static std::map<K, V> fromJson(const QJsonValue& json) {
		std::map<K, V> result;
		if (json.isObject()) {
			QJsonObject jsonObject = json.toObject();
			for (auto it = jsonObject.begin(); it != jsonObject.end(); ++it) {
				K key = ToJsonValue<K>::convert(it.key()).toVariant().value<K>();
				V value = Serializer<V>::fromJson(it.value());
				result.insert({ key, value });
			}
		}
		return result;
	}
};

#define JSON_SERIALIZABLE \
    virtual const QMetaObject* metaObject() const { \
    \
        return &this->staticMetaObject; \
    }

// Base class for serializable objects
class JsonSerializable {
	Q_GADGET
	JSON_SERIALIZABLE
public:
	virtual ~JsonSerializable() = default;
	// Convert QJsonValue in QJsonDocument as QByteArray.
	static QByteArray toByteArray(const QJsonValue& value) {
		return QJsonDocument(value.toObject()).toJson();
	}
	// Serialize all accessed JSON propertyes for this object.
	QJsonObject toJson() const {
		QJsonObject json;
		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

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

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

	// Deserialize all accessed JSON 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

				QString propertyName = metaObject()->property(i).name();
				for (const auto& key : json.keys())
				{
					if (key.compare(propertyName, Qt::CaseInsensitive) == 0)
					{
						metaObject()->property(i).writeOnGadget(this, json.value(key));
						break;
					}
				}
			}
		}
	}
	// Deserialize all accessed JSON propertyes for this object.
	void fromJson(const QByteArray& data) {
		fromJson(QJsonDocument::fromJson(data).object());
	}
};

// Serializer for custom types inheriting from JsonSerializable
template<typename T>
struct Serializer<T, typename std::enable_if<std::is_base_of<JsonSerializable, T>::value>::type> {
	static QJsonValue toJson(const T& value) {
		return value.toJson();
	}

	static T fromJson(const QJsonValue& json) {
		T result;
		result.fromJson(json.toObject());
		return result;
	}
};

// Macros for easy property declaration
#define JSON_PROPERTY(type, name) \
    Q_PROPERTY(QJsonValue name READ get_##json##_##name WRITE set_##json##_##name) \
    private: \
        type m_##name; \
        QJsonValue get_##json##_##name() const { return Serializer<type>::toJson(m_##name); } \
        void set_##json##_##name(const QJsonValue& value) { m_##name = Serializer<type>::fromJson(value); } \
    public: \
        type name() const { return m_##name; } \
        void set_##name(const type& value) { m_##name = value; }

#endif // JSON_SERIALIZER_H

在现代软件开发中,JSON(JavaScript Object Notation)已成为一种广泛使用的数据交换格式。本文将介绍如何在C++中使用Qt框架实现一个灵活且易用的JSON序列化和反序列化系统。

核心思想

这个序列化系统的核心思想是:

  1. 使用模板和特化来处理不同类型的序列化和反序列化。
  2. 利用Qt的元对象系统来自动化序列化过程。
  3. 提供一个基类JsonSerializable和宏来简化使用。

关键组件

1. Serializer模板

template<typename T, typename Enable = void>
struct Serializer;

这是整个系统的核心。它为不同的类型提供了toJsonfromJson方法。通过特化这个模板,我们可以为不同的类型定义自定义的序列化行为。

2. ToJsonValue

template<typename T>
struct ToJsonValue {
    static QJsonValue convert(const T& value) {
        return QJsonValue::fromVariant(QVariant::fromValue(value));
    }
};

这个辅助结构体用于将各种类型转换为QJsonValue

3. JsonSerializable基类

class JsonSerializable {
    Q_GADGET
    JSON_SERIALIZABLE
public:
    QJsonObject toJson() const;
    void fromJson(const QJsonValue& val);
    // ...其他方法...
};

这个基类提供了基本的序列化和反序列化功能。它利用Qt的元对象系统来自动化处理对象的属性。

4. JSON_PROPERTY宏

#define JSON_PROPERTY(type, name) \
    // ... 宏定义 ...

这个宏简化了在派生类中声明可序列化属性的过程。

支持的类型

这个系统支持多种类型:

  • 基本类型 (int, float, QString等)
  • Qt容器 (QList, QVector, QMap, QHash)
  • 标准库容器 (std::vector, std::map)
  • 自定义类型 (只要它们从JsonSerializable派生)

优点

  1. 类型安全: 使用模板和类型特化确保类型安全。
  2. 灵活性: 容易扩展以支持新的类型。
  3. 易用性: 使用宏和基类简化了使用过程。
  4. 自动化: 利用Qt的元对象系统自动处理属性。

使用方法:序列化和反序列化

#ifndef TEST_PERSON_H
#define TEST_PERSON_H

#include "JsonSerializer.h"

class TestPerson final: public JsonSerializable
{
	Q_GADGET
	JSON_SERIALIZABLE
public:
	JSON_PROPERTY(QString, name)
	JSON_PROPERTY(int, age)
	JSON_PROPERTY(QList<QString>, hobbies)
};
#endif // !PERSON_TEST_H

#ifndef TEST_PAGE_INFO_H
#define TEST_PAGE_INFO_H

#include "JsonSerializer.h"

class TestPageInfo final: public JsonSerializable
{
	Q_GADGET
	JSON_SERIALIZABLE
public:
	JSON_PROPERTY(int, totalNumber)
	JSON_PROPERTY(int, totalPage)
	JSON_PROPERTY(int, pageSize)
	JSON_PROPERTY(int, currentPage)
};
#endif // !TEST_PAGE_INFO_H

#ifndef TEST_PAGED_PERSON_H
#define TEST_PAGED_PERSON_H

#include "JsonSerializer.h"
#include "TestPageInfo.h"
#include "TestPerson.h"

class TestPagedPerson final: public JsonSerializable
{
	Q_GADGET
	JSON_SERIALIZABLE
public:
	JSON_PROPERTY(TestPageInfo, page)
	//JSON_PROPERTY(QVector<TestPerson>, persons)
	JSON_PROPERTY(std::vector<TestPerson>, persons)
};
#endif // !TEST_PAGED_PERSON_H
TestPagedPerson pagedPerson;
TestPageInfo page;
page.set_totalNumber(80);
page.set_totalPage(4);
page.set_currentPage(1);
page.set_pageSize(80 / 4);
pagedPerson.set_page(page);
TestPerson person1;
person1.set_age(18);
person1.set_name("A");
person1.set_hobbies({ "running", "TV" });
TestPerson person2;
person2.set_age(16);
person2.set_name("B");
person2.set_hobbies({ "reading", "swimming" });
TestPerson person3;
person3.set_age(21);
person3.set_name("C");
person3.set_hobbies({ "gaming", "swimming" });
pagedPerson.set_persons({ person1, person2, person3 });
auto rawJson = pagedPerson.toRawJson();
qDebug().noquote() << rawJson;

TestPagedPerson newPagedPerson;
newPagedPerson.fromJson(rawJson);

标签:QJsonValue,const,Qt,json,value,JSON,C++,序列化,Serializer
From: https://www.cnblogs.com/linxmouse/p/18439977

相关文章

  • 【C++】继承(下)
    个人主页~继承(上)~继承四、派生类的默认成员函数五、继承与友元六、继承与静态成员七、复杂的菱形继承以及菱形虚拟继承1、菱形继承2、菱形虚拟继承八、继承的总结与反思继承和组合四、派生类的默认成员函数派生类的构造函数必须调用基类的构造函数初始化基类的......
  • C++ -引用-详解
    博客主页:【夜泉_ly】本文专栏:【C++】欢迎点赞......
  • 分享C++程序员面试八股文(十三)
    以下是C++常见八股文(十三):一、C++中的命名空间和模块的高级用法(AdvancedUsageofNamespacesandModules)解释命名空间别名和嵌套命名空间的作用及使用场景命名空间别名:作用:命名空间别名可以为一个较长或复杂的命名空间提供一个更简洁的名称,提高代码的可读性和可......
  • C++-练习-46
    题目:许多州的彩票发行机构都使用如下所示程序的简单彩票的变体。在这些玩法中,玩家从一组被称为域号码的号码中选择几个。列如,可以从域号码1~47中选择5个号码;还可以从第二个区间(如1~27)选择一个号码(称为特选号码)。要赢得头奖,必须正确猜中所有的号码。中头奖的几率是选择所有域号......
  • C++ 静态顺序表和动态顺序表
    对比静态顺序表与动态顺序表特性静态顺序表动态顺序表大小固定动态内存管理简单复杂随机访问快速快速插入/删除效率较低较低(需移动元素)扩展能力不可扩展可扩展C++静态顺序表概述定义:静态顺序表是一种线性表的实现方式,采用一段连续的内存空间存储数据元素,具有固定的大小。在......
  • 南沙C++信奥老师解一本通题 1221:分成互质组
    ​ 【题目描述】给定n个正整数,将它们分组,使得每组中任意两个数互质。至少要分成多少个组?【输入】第一行是一个正整数n。1≤n≤10。第二行是n个不大于10000的正整数。【输出】一个正整数,即最少需要的组数。【输入样例】6142033117143175【输出样例】3......
  • .NET 开源高性能 MQTT 类库
    阅读目录前言项目介绍功能说明功能特点应用场景使用方法项目地址总结最后前言随着物联网(IoT)技术的迅猛发展,MQTT(消息队列遥测传输)协议凭借其轻量级和高效性,已成为众多物联网应用的首选通信标准。MQTTnet作为一个高性能的.NET开源库,为.NET平台上的MQTT客户端......
  • C++:模板初级
    一.泛型编程。1.1如何实现一个交换函数呢?voidSwap(int&left,int&right){ inttemp=left; left=right; right=temp;}voidSwap(double&left,double&right){ doubletemp=left; left=right; right=temp;}voidSwap(char&left,......
  • C++ 学习,标准库
    C++标准库是C++语言的重要组成部分,它提供了一系列的类、函数和模板,使得开发者能够更加高效地进行编程。C++标准库包括一组头文件,头文件提供了各种功能和工具,涵盖了输入输出、容器、算法、多线程、正则表达式等。C++标准库可以分为两部分:标准函数库: 由通用的、独立的、......
  • 【C++掌中宝】用最少的话让你全方位理解内联函数
    文章目录引言1.什么是内联函数2.工作原理3.内联函数的编程风格4.使用限制5.内联函数与宏的比较6.优缺点7.何时使用内联函数8.补充9.总结结语引言在C++编程中,函数的调用开销是程序运行效率的一个重要影响因素。为了解决频繁调用函数时的性能问题,C++提供了内......