C++中使用Qt实现JSON序列化与反序列化
// File: JsonSerializer // Author: linxmouse@gmail.com // 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序列化和反序列化系统。
核心思想
这个序列化系统的核心思想是:
- 使用模板和特化来处理不同类型的序列化和反序列化。
- 利用Qt的元对象系统来自动化序列化过程。
- 提供一个基类
JsonSerializable
和宏来简化使用。
关键组件
1. Serializer模板
template<typename T, typename Enable = void> struct Serializer;
这是整个系统的核心。它为不同的类型提供了toJson
和fromJson
方法。通过特化这个模板,我们可以为不同的类型定义自定义的序列化行为。
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派生)
优点
- 类型安全: 使用模板和类型特化确保类型安全。
- 灵活性: 容易扩展以支持新的类型。
- 易用性: 使用宏和基类简化了使用过程。
- 自动化: 利用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);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗