Qt Json 序列化操作

cpp 复制代码
#ifndef UTILS_JSON_H
#define UTILS_JSON_H

#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonValue>
#include <QFile>
#include <QVariant>
#include <QString>
#include <functional>
#include <cstddef>
#include <QSaveFile>
#include <type_traits>
#include <algorithm> // for std::min if needed, though qMin is Qt

namespace Utils {
    namespace JsonSerialize {

        // --- 数组转换 ---

        template< typename T, std::size_t N >
        inline QJsonArray ToJsonArray(const T(&arr)[N]) {
            QJsonArray result;
            for (std::size_t i = 0; i < N; ++i) {
                result.append(QJsonValue(arr[i]));
            }
            return result;
        }

        template<typename T, std::size_t N>
        inline void FromJsonArray(const QJsonArray& jarr, T(&arr)[N]) {
            const int len = std::min(static_cast<int>(jarr.size()), static_cast<int>(N));
            for (int i = 0; i < len; ++i) {
                arr[i] = static_cast<T>(jarr[i].toDouble());
            }
        }

        template <std::size_t N>
        inline void FromJsonArray(const QJsonArray& jarr, int(&arr)[N]) {
            const int len = std::min(static_cast<int>(N), jarr.size());
            for (int i = 0; i < len; ++i)
                arr[i] = jarr[i].toInt();
        }

        // --- 基础类型 jsonField (Write) ---

        inline void jsonField(QJsonObject& obj, const char* key, int val) {
            obj[key] = val;
        }
        inline void jsonField(QJsonObject& obj, const char* key, float val) {
            obj[key] = static_cast<double>(val);
        }
        inline void jsonField(QJsonObject& obj, const char* key, bool val) {
            obj[key] = val;
        }
        inline void jsonField(QJsonObject& obj, const char* key, unsigned char val) {
            obj[key] = static_cast<int>(val);
        }
        template <typename T, std::size_t N>
        inline void jsonField(QJsonObject& obj, const char* key, const T(&arr)[N]) {
            obj[key] = ToJsonArray(arr);
        }

        // --- 基础类型 jsonField (Read) ---

        inline void jsonField(const QJsonObject& obj, const char* key, int& val) {
            if (obj.contains(key)) val = obj[key].toInt();
        }
        inline void jsonField(const QJsonObject& obj, const char* key, float& val) {
            if (obj.contains(key)) val = static_cast<float>(obj[key].toDouble());
        }
        inline void jsonField(const QJsonObject& obj, const char* key, bool& val) {
            if (obj.contains(key)) val = obj[key].toBool();
        }
        inline void jsonField(const QJsonObject& obj, const char* key, unsigned char& val) {
            if (obj.contains(key)) val = static_cast<unsigned char>(obj[key].toInt());
        }
        template <typename T, std::size_t N>
        inline void jsonField(const QJsonObject& obj, const char* key, T(&arr)[N]) {
            if (obj.contains(key)) FromJsonArray(obj[key].toArray(), arr);
        }

        // --- 辅助宏 ---

        /**
         * @brief 安全地去除 m_ 前缀
         */
        inline const char* StripM_(const char* s) {
            if (!s) return s;
            // 检查 "m_" 前缀
            if (s[0] == 'm' && s[1] == '_') {
                return s + 2;
            }
            return s;
        }

        /**
         * @brief 字段宏:依赖上下文中的 obj (QJsonObject) 和 p (Struct&)
         */
        #define JF(member) jsonField(obj, StripM_(#member), p.member);

        /**
         * @brief 嵌套对象支持宏
         */
        #define JSON_NESTED(Type)                                                      \
          inline void jsonField(QJsonObject &obj, const char *key, const Type &val) {  \
            obj[key] = ToJson(val);                                                    \
          }                                                                            \
          inline void jsonField(const QJsonObject &obj, const char *key, Type &val) {  \
            if (obj.contains(key))                                                     \
              FromJson(obj[key].toObject(), val);                                      \
          }

        /**
         * @brief 定义序列化/反序列化函数
         */
        #define DEFINE_JSON(Type, ...)                                                 \
          inline QJsonObject ToJson(const Type &p) {                                   \
            QJsonObject obj;                                                           \
            __VA_ARGS__                                                                \
            return obj;                                                                \
          }                                                                            \
          inline void FromJson(const QJsonObject &obj, Type &p) {                      \
            __VA_ARGS__                                                                \
          }                                                                            \
          JSON_NESTED(Type)

        // --- SFINAE 检测 ---

        template <typename T, typename = void>
        struct has_to_json : std::false_type {};

        template<typename T>
        struct has_to_json<T, std::void_t< decltype(ToJson(std::declval<const T&>())) >> : std::true_type {};

        template<typename T>
        constexpr bool has_to_json_v = has_to_json<T>::value;

        template <typename T, typename = void>
        struct has_from_json : std::false_type {};

        template<typename T>
        struct has_from_json<T, std::void_t< decltype(FromJson(std::declval<const QJsonObject&>(), std::declval<T&>())) >> : std::true_type {};

        template<typename T>
        constexpr bool has_from_json_v = has_from_json<T>::value;

        // --- 文件 IO ---

        template <typename T, std::enable_if_t<has_to_json_v<T>, int> = 0>
        bool SaveToFile(const T& obj, const QString& filepath) {
            QSaveFile file(filepath);
            if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
                return false;
            }
            QJsonDocument doc(ToJson(obj));
            qint64 written = file.write(doc.toJson(QJsonDocument::Indented));
            if (!file.commit()) {
                return false;
            }
            return written > 0;
        }

        template <typename T, std::enable_if_t<has_from_json_v<T>, int> = 0>
        bool LoadFromFile(T& param, const QString& filePath) {
            QFile file(filePath);
            if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
                return false;

            QByteArray raw = file.readAll();
            file.close();

            if (raw.isEmpty()) {
                return false;
            }

            QJsonParseError err;
            QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
            if (err.error != QJsonParseError::NoError || !doc.isObject())
                return false;

            FromJson(doc.object(), param);
            return true;
        }

    } // namespace JsonSerialize
} // namespace Utils

#endif // UTILS_JSON_H

用法

cpp 复制代码
using namespace Utils::JsonSerialize;

struct ArrT {
    int m_size = 0;
    int m_data = 0;
};

//// 生成 ArrT 的序列化代码
DEFINE_JSON(ArrT,
    JF(m_size)
    JF(m_data)
)

// ============================================================================
// 主函数
// ============================================================================
#include <QCoreApplication>
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    qDebug() << "--- JSON Serialization Test ---";

    // 1. 创建测试对象
    ArrT myArr;
    myArr.m_size = 100;
    myArr.m_data = 200;

    QString testFilePath = "test_config.json";

    // 2. 保存到文件
    if (Utils::JsonSerialize::SaveToFile(myArr, testFilePath)) {
        qDebug() << "Success: Saved to" << testFilePath;
    } else {
        qWarning() << "Error: Failed to save file";
    }

    // 3. 从文件加载
    ArrT loadedArr;
    if (Utils::JsonSerialize::LoadFromFile(loadedArr, testFilePath)) {
        qDebug() << "Success: Loaded from file";
        qDebug() << "  Size:" << loadedArr.m_size;
        qDebug() << "  Data:" << loadedArr.m_data;
    } else {
        qWarning() << "Error: Failed to load file";
    }

    // 4. 启动 Qt 事件循环 (使用 exec 而不是 run)
    return app.exec();
}
相关推荐
Frank_refuel15 分钟前
C++11之一场名为“搬家”的 C++ 之旅
开发语言·c++
Wang's Blog1 小时前
Java 项目实战: 外卖平台优化-Nginx配置文件结构与块层级
java·开发语言·nginx
2601_962071572 小时前
类变量和全局变量的查找路径有什么区别?
开发语言·python
\光辉岁月/3 小时前
5.java-数组
java·开发语言
Java后端的Ai之路3 小时前
Python进阶探索29_eval内置函数
开发语言·python·探索·eval·内置函数
Quz3 小时前
QML PathView:吸附对齐与环形轮播
qt
谢亮_vipxieliang3 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
时间的拾荒人3 小时前
Qt 多线程详解:从 QThread 到实战
开发语言·qt·面试
郑州光合科技余经理3 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
2601_962071573 小时前
类变量和全局变量的生命周期有什么区别?
java·开发语言·jvm