// 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序列化和反序列化系统。
核心思想
这个序列化系统的核心思想是:
- 使用模板和特化来处理不同类型的序列化和反序列化。
- 利用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);
标签:QJsonValue,const,Qt,json,value,JSON,C++,序列化,Serializer
From: https://www.cnblogs.com/linxmouse/p/18439977