Qt序列化与持久化深度解析:从QDataStream到自定义二进制协议

对象如何保存到文件?深入Qt序列化机制,掌握跨平台数据持久化的核心技巧

一、序列化概述

序列化是将对象状态转换为可存储或传输格式的过程,反序列化则是从存储格式还原对象。Qt提供了完整的序列化框架,支持二进制和文本两种格式。

1.1 Qt序列化核心类

类名 格式 特点 适用场景
QDataStream 二进制 高效、跨平台 本地存储、网络传输
QTextStream 文本 可读性好 配置文件、日志
QJsonObject JSON 通用格式 Web交互、配置
QXmlStreamWriter XML 标准格式 数据交换

1.2 源码位置

复制代码
qtbase/src/corelib/serialization/
├── qdatastream.cpp/h           # 二进制流
├── qtextstream.cpp/h           # 文本流
├── qjsondocument.cpp/h         # JSON文档
├── qjsonobject.cpp/h           # JSON对象
├── qxmlstream.cpp/h            # XML流
└── qcborvalue.cpp/h            # CBOR格式

二、QDataStream二进制序列化

2.1 基本类型序列化

cpp 复制代码
#include <QDataStream>
#include <QFile>

// 写入基本类型
void writeBasicTypes()
{
    QFile file("data.bin");
    if (!file.open(QIODevice::WriteOnly)) return;
    
    QDataStream out(&file);
    
    // 设置版本(确保跨版本兼容)
    out.setVersion(QDataStream::Qt_6_0);
    
    // 写入各种类型
    out << 42;                    // int
    out << 3.14159;               // double
    out << true;                  // bool
    out << QString("Hello Qt");   // QString
    out << QByteArray("Binary");  // QByteArray
    
    file.close();
}

// 读取基本类型
void readBasicTypes()
{
    QFile file("data.bin");
    if (!file.open(QIODevice::ReadOnly)) return;
    
    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_6_0);
    
    int i;
    double d;
    bool b;
    QString s;
    QByteArray ba;
    
    in >> i >> d >> b >> s >> ba;
    
    qDebug() << i << d << b << s << ba;
    file.close();
}

2.2 自定义对象序列化

cpp 复制代码
// 定义自定义类
class User
{
public:
    QString name;
    int age;
    QString email;
    QList<QString> tags;
    
    // 序列化方法
    friend QDataStream& operator<<(QDataStream& out, const User& user)
    {
        out << user.name;
        out << user.age;
        out << user.email;
        out << user.tags;
        return out;
    }
    
    // 反序列化方法
    friend QDataStream& operator>>(QDataStream& in, User& user)
    {
        in >> user.name;
        in >> user.age;
        in >> user.email;
        in >> user.tags;
        return in;
    }
};

// 使用
void saveUser()
{
    User user;
    user.name = "张三";
    user.age = 28;
    user.email = "zhangsan@example.com";
    user.tags = {"Qt", "C++", "Python"};
    
    QFile file("user.bin");
    file.open(QIODevice::WriteOnly);
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_6_0);
    out << user;
    file.close();
}

void loadUser()
{
    User user;
    QFile file("user.bin");
    file.open(QIODevice::ReadOnly);
    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_6_0);
    in >> user;
    file.close();
    
    qDebug() << user.name << user.age << user.email << user.tags;
}

2.3 容器序列化

cpp 复制代码
// Qt容器自动支持序列化
void serializeContainers()
{
    QFile file("containers.bin");
    file.open(QIODevice::WriteOnly);
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_6_0);
    
    // QList
    QList<int> intList = {1, 2, 3, 4, 5};
    out << intList;
    
    // QMap
    QMap<QString, int> scoreMap;
    scoreMap["Alice"] = 95;
    scoreMap["Bob"] = 87;
    out << scoreMap;
    
    // QHash
    QHash<QString, QString> cityMap;
    cityMap["Beijing"] = "北京";
    cityMap["Shanghai"] = "上海";
    out << cityMap;
    
    // 嵌套容器
    QList<QMap<QString, int>> complexData;
    complexData.append(scoreMap);
    out << complexData;
    
    file.close();
}

2.4 版本兼容性

cpp 复制代码
// 使用版本号确保向后兼容
class Config
{
public:
    int version = 2;  // 当前版本
    QString name;
    int value;
    QString description;  // v2新增字段
    
    friend QDataStream& operator<<(QDataStream& out, const Config& c)
    {
        out << c.version;
        out << c.name;
        out << c.value;
        out << c.description;
        return out;
    }
    
    friend QDataStream& operator>>(QDataStream& in, Config& c)
    {
        in >> c.version;
        in >> c.name;
        in >> c.value;
        
        // 根据版本判断是否读取新字段
        if (c.version >= 2) {
            in >> c.description;
        } else {
            c.description = "";  // 默认值
        }
        
        return in;
    }
};

三、JSON序列化

3.1 基本JSON操作

cpp 复制代码
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

void writeJson()
{
    // 创建JSON对象
    QJsonObject root;
    root["name"] = "张三";
    root["age"] = 28;
    root["active"] = true;
    
    // 嵌套对象
    QJsonObject address;
    address["city"] = "北京";
    address["street"] = "朝阳路";
    root["address"] = address;
    
    // 数组
    QJsonArray tags;
    tags.append("Qt");
    tags.append("C++");
    root["tags"] = tags;
    
    // 写入文件
    QJsonDocument doc(root);
    QFile file("config.json");
    file.open(QIODevice::WriteOnly);
    file.write(doc.toJson(QJsonDocument::Indented));
    file.close();
}

void readJson()
{
    QFile file("config.json");
    file.open(QIODevice::ReadOnly);
    
    QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
    QJsonObject root = doc.object();
    
    QString name = root["name"].toString();
    int age = root["age"].toInt();
    bool active = root["active"].toBool();
    
    QJsonObject address = root["address"].toObject();
    QString city = address["city"].toString();
    
    QJsonArray tags = root["tags"].toArray();
    for (const QJsonValue &tag : tags) {
        qDebug() << tag.toString();
    }
    
    file.close();
}

3.2 自定义对象JSON序列化

cpp 复制代码
class User
{
public:
    QString name;
    int age;
    QList<QString> tags;
    
    QJsonObject toJson() const
    {
        QJsonObject obj;
        obj["name"] = name;
        obj["age"] = age;
        
        QJsonArray tagsArray;
        for (const QString &tag : tags) {
            tagsArray.append(tag);
        }
        obj["tags"] = tagsArray;
        
        return obj;
    }
    
    static User fromJson(const QJsonObject &obj)
    {
        User user;
        user.name = obj["name"].toString();
        user.age = obj["age"].toInt();
        
        QJsonArray tagsArray = obj["tags"].toArray();
        for (const QJsonValue &tag : tagsArray) {
            user.tags.append(tag.toString());
        }
        
        return user;
    }
};

四、QSettings配置持久化

4.1 基本使用

cpp 复制代码
#include <QSettings>

void saveSettings()
{
    QSettings settings("MyCompany", "MyApp");
    
    settings.setValue("window/size", QSize(800, 600));
    settings.setValue("window/position", QPoint(100, 100));
    settings.setValue("user/name", "张三");
    settings.setValue("user/remember", true);
    settings.setValue("recent/files", QStringList() << "file1.txt" << "file2.txt");
}

void loadSettings()
{
    QSettings settings("MyCompany", "MyApp");
    
    QSize size = settings.value("window/size").toSize();
    QPoint pos = settings.value("window/position").toPoint();
    QString name = settings.value("user/name").toString();
    bool remember = settings.value("user/remember", false).toBool();  // 默认值
    QStringList recent = settings.value("recent/files").toStringList();
}

4.2 自定义格式

cpp 复制代码
// 使用INI格式
QSettings settings("config.ini", QSettings::IniFormat);
settings.setValue("General/Language", "zh_CN");
settings.setValue("General/Theme", "Dark");

// 使用注册表(Windows)
QSettings settings("HKEY_CURRENT_USER\Software\MyApp", QSettings::NativeFormat);

五、性能优化

5.1 批量序列化

cpp 复制代码
// 使用事务提高性能
void batchSerialize(const QList<User> &users)
{
    QFile file("users.bin");
    file.open(QIODevice::WriteOnly);
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_6_0);
    
    // 先写入数量
    out << users.size();
    
    // 批量写入
    for (const User &user : users) {
        out << user;
    }
    
    file.close();
}

5.2 压缩序列化数据

cpp 复制代码
#include <QBuffer>
#include <QByteArray>
#include <zlib.h>

QByteArray compressData(const QByteArray &data)
{
    QByteArray compressed;
    compressed.resize(data.size() + data.size() / 100 + 12);
    
    uLongf destLen = compressed.size();
    compress2((Bytef*)compressed.data(), &destLen,
              (const Bytef*)data.constData(), data.size(),
              Z_BEST_COMPRESSION);
    
    compressed.resize(destLen);
    return compressed;
}

六、总结

Qt序列化核心要点:

  1. QDataStream:高效的二进制序列化,支持版本兼容
  2. QJsonObject:通用的JSON序列化,适合Web交互
  3. QSettings:轻量级配置持久化
  4. 版本控制:使用版本号确保向后兼容

《注:若有发现问题欢迎大家提出来纠正》

相关推荐
誰能久伴不乏6 小时前
Qt/C++ 架构之美:用一个“水龙头”隐喻,讲透面向接口编程与彻底解耦
c++·qt·架构
周末也要写八哥6 小时前
Golang语言与Rust语言的对比
开发语言·后端·golang
秋96 小时前
一键安装mysql8.4.9(附脚本)
数据库
楼田莉子6 小时前
Linux网络:数据链路层
linux·服务器·开发语言·网络·c++·后端
不甘先生6 小时前
Go 四层架构实战:Handler + Service + Repository + Entity(清晰、可控、可演进)
开发语言·架构·golang
Yang-Never6 小时前
Git -> Git Worktree 工作树
android·开发语言·git·android studio
zjy277776 小时前
Go语言怎么用GitHub Actions_Go语言GitHub Actions教程【基础】
jvm·数据库·python
2301_782040456 小时前
如何实现SQL用户行为追踪_通过触发器记录操作明细
jvm·数据库·python
riNt PTIP6 小时前
GO 快速升级Go版本
开发语言·redis·golang