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. 版本控制:使用版本号确保向后兼容

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

相关推荐
破茧蝴蝶2 小时前
Python分布式爬虫完整教程
开发语言·python
数据库小学妹2 小时前
国产数据库怎么选?兼容、迁移、核心案例与融合架构四道关
数据库·架构·国产数据库·数据库迁移·融合数据库
狗凯之家源码网2 小时前
全新 PHP 轻量客服聊天室源码深度评测
开发语言·php·聊天室系统
远离UE43 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience3 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
IvorySQL3 小时前
PostgreSQL 日报| PgBouncer 修复三个 CVE 漏洞(10 月 15 日)
数据库·postgresql·区块链
饺子大魔王的男人3 小时前
后台表格、查询、流程总在重复写?我用ToolJet把PostgreSQL直接做成可远程访问的业务系统
数据库·postgresql
Gauss松鼠会4 小时前
高斯数据库(openGauss)的物理存储结构
运维·服务器·数据库·sql·opengauss·经验总结
栈帧之外4 小时前
【C++】STL之set
开发语言·c++