Qt C++ 项目实战JSON格式文件解析存取

概要

        记录一下我在工作项目中的文件读取,项目之前我采用的是二进制格式来进行文件的存储,但是由于不好进行版本控制、扩展、添加新的功能/字段信息等,所以我改成使用JSON格式来进行文件的存取,解决了以上问题,并且扩展方便,目前我开发的软件要读取的文件类型有二进制、字符串文本、JSON以及Excel格式多接口进行不同格式读取,存储我都统一使用JSON来进行。

JSON解析过程

QJsonDocment库

刚开始我使用的是Qt自带的QJsonDocment库来进行数据的解析编译,代码如下所示:

这是我软件中的一个坐标轴结构体,以此举例进行JSON存取。

/*******************
 * @author 范儿
 * @description 左Y轴
*******************/
struct LeftYAxisType {

    bool isExecute = false;//是否执行过
    int type = 3; //类型

    double ylower = 1; //最小值
    double yupper = 10; //最大值

    bool isDisplay = true; //是否显示
    bool AutoScale = false; //是否自适应
    bool LogScale = true; //是否对数轴

    QString axisLabel = "";//轴标题
    QString labelColor = ""; //颜色
    QString labelFont = ""; //字体
    QString labelFormat = ""; //坐标轴标签类型

};

 以下是保存方法的逐级调用:

/*******************
 * @author 范儿
 * @method saveToFile
 * @param fileName(文件路径名称)password(设置密码)
 * @description 文件数据保存
*******************/
bool  MiddleDocument::saveToFile(QString  fileName, QString password){

    QJsonObject  jContent;

    int lastDotIndex = fileName.lastIndexOf('.');
    QString saveFilePath = fileName.left(lastDotIndex);
    saveFilePath.append(".HFWX");

    if(saveToJson(jContent)){
        // 将 QJsonObject 转换为 QJsonDocument
        QJsonDocument jsonDoc(jContent);
        // 将 QJsonDocument 写入文件
        QFile file(saveFilePath);
        if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
            file.write(jsonDoc.toJson());
            file.close();
        } else {
            QMessageBox::warning(NULL,"writing","Failed to open file for writing.");
        }
        return true;
    }
    return false;
}
/*******************
 * @author 范儿
 * @method
 * @param
 * @description 保存文件数据
*******************/
bool  MiddleDocument::saveToJson(QJsonObject& json){

    //实际的保存逻辑编写
    //......


    //举例: 保存图像数据
    json = saveToJsonByLeftYAxis(json);
    return true;
}
/**
 * @brief CustomplotImage::saveToJsonByLeftYAxis
 * @param newJson
 * @return newJson
 *  保存左Y轴数据
 */
QJsonObject CustomplotImage::saveToJsonByLeftYAxis(QJsonObject& newJson){

    QJsonObject json;

    json.insert("isExecute", leftYAxisType.isExecute);
    json.insert("type", leftYAxisType.type);
    json.insert("ylower", leftYAxisType.ylower);
    json.insert("yupper", leftYAxisType.yupper);
    json.insert("isDisplay", leftYAxisType.isDisplay);
    json.insert("AutoScale", leftYAxisType.AutoScale);
    json.insert("LogScale", leftYAxisType.LogScale);
    json.insert("axisLabel", leftYAxisType.axisLabel);
    json.insert("labelColor", leftYAxisType.labelColor);
    json.insert("labelFont", leftYAxisType.labelFont);
    json.insert("labelFormat", leftYAxisType.labelFormat);

    newJson.insert("leftYAxis", json);
    return newJson;
}

 下面图片就是存储到文件中的样子:

 

 然后我们可以以同样的方式进行读取:

/*******************
 * @author 范儿
 * @method loadFromFile
 * @param filePaht文件路径
 * @description 加载文件数据
*******************/
bool MiddleDocument::loadFromFile(QString  filePaht){
    bool  ret = true;
    // 打开 JSON 文件
    QFile file(filePaht);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Failed to open file for reading.";
        return false;
    }

    // 读取文件内容
    QByteArray jsonData = file.readAll();
    file.close(); //释放资源

    // 将 JSON 数据解析为 QJsonDocument
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);

    // 检查是否成功解析
    if (jsonDoc.isNull() || !jsonDoc.isObject()) {
        qDebug() << "Invalid JSON document.";
        return false;
    }

    // 获取根 JSON 对象
    QJsonObject jsonObj = jsonDoc.object();
    ret = loadFromJSON(jsonObj);
    return ret;
}
/**
 * @brief CustomplotImage::loadFromJsonByLeftYAxis
 * @param json
 * @return
 * 加载左Y轴数据
 */
void CustomplotImage::loadFromJsonByLeftYAxis(QJsonObject& newJson) {
    QJsonObject json;
    if(newJson.contains("leftYAxis")){
        json = newJson["leftYAxis"].toObject();
    }else{
        return;
    }

    leftYAxisType.isExecute = json["isExecute"].toBool();
    leftYAxisType.type = json["type"].toInt();
    leftYAxisType.ylower = json["ylower"].toDouble();
    leftYAxisType.yupper = json["yupper"].toDouble();
    leftYAxisType.isDisplay = json["isDisplay"].toBool();
    leftYAxisType.AutoScale = json["AutoScale"].toBool();
    leftYAxisType.LogScale = json["LogScale"].toBool();
    leftYAxisType.axisLabel = json["axisLabel"].toString();
    leftYAxisType.labelColor = json["labelColor"].toString();
    leftYAxisType.labelFont = json["labelFont"].toString();
    leftYAxisType.labelFormat = json["labelFormat"].toString();
}

 如果有数组进行存储,可以采用Qt 中的模版函数进行封装,避免代码冗余,不然就得在方法中循环的去进行JSON,我一开始就是如此,为此我将代码抽取封装起来,如下方法所示:

    // 辅助函数,将QVector<T>转换为QJsonArray
    template<typename T>
    QJsonArray toJsonArray(const QVector<T>& data) {
        QJsonArray array;
        for (const auto& value : data) {
            array.append(value);
        }
        return array;
    }

    // 辅助函数,将QJsonArray转换为QVector<T>
    template<typename T>
    QVector<T> fromJsonArray(const QJsonArray& array) {
        QVector<T> data;
        for (const QJsonValue& value : array) {
            data.push_back(value.toVariant().value<T>());
        }
        return data;
    }

 使用举例:

struct AverageLabels {
   QVector<double> areaValue; //area值
   QVector<double> SDValue; //SD值
   QVector<double> MinValue; //Min值
   QVector<double> MaxValue; //Max值
}
QJsonObject RealCurve::saveToJsonByAverageLabel(QJsonObject& newJson){
    averageLabelJson["areaValue"] = toJsonArray(averageLabels.areaValue);
    averageLabelJson["SDValue"] = toJsonArray(averageLabels.SDValue);
    averageLabelJson["MinValue"] = toJsonArray(averageLabels.MinValue);
    averageLabelJson["MaxValue"] = toJsonArray(averageLabels.MaxValue);
    return newJson;
}

bool RealCurve::loadFromJsonByAverageLabel(QJsonObject& newJson){
    averageLabels.areaValue = fromJsonArray<double>(json["areaValue"].toArray());
    averageLabels.SDValue = fromJsonArray<double>(json["SDValue"].toArray());
    averageLabels.MinValue = fromJsonArray<double>(json["MinValue"].toArray());
    averageLabels.MaxValue = fromJsonArray<double>(json["MaxValue"].toArray());
    return true;
}

 因为需求有提到可能后面会对文件进行加密存储,添加密码控制,持有密码者才能读取文件,但目前并没有实际进行运用,所以我先预留设计了一个方案用于保留,效果还没检验,仅供参考。

/*******************
 * @author 范儿
 * @method saveToFile
 * @param fileName(文件路径名称)password(设置密码)
 * @description 文件数据保存
*******************/
bool  MiddleDocument::saveToFile(QString  fileName, QString password){
        QJsonObject  jContent;

        if(saveToJson(jContent)){

            QByteArray content = asBytes();
            QByteArray  contentHash = QCryptographicHash::hash(content, QCryptographicHash::Sha256);
            if(password.size() > 0){
                RC4(content, password);
            }

            QZipWriter writer(fileName);
            writer.addFile("content.json", content);
            writer.addFile("contentHashInfo", contentHash);
            writer.close();
            return true;
        }
        return false;

}

bool  MiddleDocument::loadFromFile(QString  fileName, std::function<void(QString& password)> onPassword){
    bool  ret = false;
    QFile  file(fileName);
    // 读取文件内容
    if(file.exists()){
        QZipReader  reader(fileName, QIODevice::ReadOnly);

        QVector<QZipReader::FileInfo> fileInfoList = reader.fileInfoList();

        int hashIndex = indexOfFileInfo("contentHashInfo",fileInfoList);
        int contentIndex = indexOfFileInfo("content.json", fileInfoList);

        if(contentIndex < 0){
            //内容文件不存在
            return false;
        }
        QByteArray  content = reader.fileData("content.json");
        QByteArray  hash = (hashIndex >=0)?reader.fileData("contentHashInfo"):"";
        QByteArray  contentHash = QCryptographicHash::hash(content, QCryptographicHash::Sha256);

        if(hash.size() > 0 && (contentHash != hash)){
            //如果存在hash文件,且内容和计算的contentHash不同,说明数据是加密过的
            QString  password;
            if(nullptr != onPassword){
                //调用密码回调
                onPassword(password);
            }
            //用密码解密
            RC4(content, password);
            //解密数据的hash值
            contentHash = QCryptographicHash::hash(content, QCryptographicHash::Sha256);
            //如果解密出来的内容的hash值和文件里面保存的hash值不同,就说明密码不正确
            if(contentHash != hash){
                return false;
            }
        }

        reader.close();
        fromBytes(content);
    }
    return ret;
}

void MiddleBase::fromBytes(const QByteArray &bytes)
{
    //将字节数据转化成JSON
    QJsonDocument  doc;
    doc.fromJson(bytes);
    QJsonObject o = doc.object();
    //在进行数据加载
    loadFromJSON(o);
}

 以上就是其中一种JSON数据存储/读取方式。后来在测试过程中,工程师觉在保存和读取上感觉有些慢,所以我就对这块在进行性能提升。首先我先对读取模块进行下手,一开始我觉得是下面这个总方法一次性进行大量存储操作导致方法占用的时间太长:

/*******************
 * @author 范儿
 * @method
 * @param
 * @description 加载全部数据(同步)
*******************/
bool RealCurve::loadFromJSON(QJsonObject& json){
    
    //每个方法都进行了大量的存储操作

    //加载功能数据
    loadFromJsonByCurveType(json);
    loadFromJsonByLayeredType(json);
    loadFromJsonByAverageLabel(json);
    loadFromJsonByDoseLabels(json);
    loadFromJsonByResterLabels(json);
    loadFromJsonBySputRateType(json);
    loadFromJsonByRsfType(json);
    loadFromJsonByNorType(json);
    loadFromJsonByMathType(json);
    loadFromJsonByIntraPoints(json);
    loadFromJsonByReducePoints(json);
    loadFromJsonByRcorSims(json);
    loadFromJsonByTruncateType(json);
    loadFromJsonBySmoothType(json);
    loadFromJsonByOffsetType(json);
    loadFromJsonByCleanType(json);
    loadFromJsonByCurveDragType(json);
    loadFromJsonByFinalDragType(json);
    loadFromJsonBySplitConvergeType(json);
    loadFromJsonByCalculations(json);
    loadFromJsonByAvergRsfType(json);
    loadFromJsonByPearkRsfType(json);
    loadFromJsonByDoseRsfType(json);
    loadFromJsonByCorrections(json);

    //加载动态参数
    //    return MiddleCurve::loadFromJSON(json);

    return true;
}

 从代码不难看出这是同步进行的,进行了一个方法后才进行下一个方法,所以我就想着直接改成异步方式进行存储,因为JSON存储不讲究顺序,于是我就改成这样: 采用Qt中的异步任务执行,这样就可以不用一个方法执行完才进行下一个方法,而是同时进行。 下面我进行了所有方法异步和分组进行异步。 

/*******************
 * @author 范儿
 * @method
 * @param
 * @description 加载全部数据(异步)
*******************/
//bool RealCurve::loadFromJSON_async(QJsonObject& json) {

//    // 用于存储各个异步任务的 future 对象
//    std::vector<std::future<bool>> futures;

//    // 启动异步任务加载功能数据,每个方法对应一个异步任务
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByCurveType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByLayeredType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByAverageLabel, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByDoseLabels, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByResterLabels, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonBySputRateType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByRsfType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByNorType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByMathType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByIntraPoints, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByReducePoints, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByRcorSims, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByTruncateType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonBySmoothType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByOffsetType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByCleanType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByCurveDragType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByFinalDragType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonBySplitConvergeType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByCalculations, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByAvergRsfType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByPearkRsfType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByDoseRsfType, this, std::ref(json)));
//    futures.push_back(std::async(std::launch::async, &RealCurve::loadFromJsonByCorrections, this, std::ref(json)));

//    // 检查每个异步任务的结果,如果有任何一个任务失败,则整体返回失败
//    for (auto& future : futures) {
//        if (!future.get()) {
//            return false;
//        }
//    }

//    // 加载动态参数(这里假设也是可以异步的,如果不行,可以单独处理)
//    // 示例代码中暂时省略具体实现,可根据实际情况补充
//    // auto dynamicParamFuture = std::async(std::launch::async, &RealCurve::loadDynamicParams, this, std::ref(json));
//    // if (!dynamicParamFuture.get()) {
//    //     return false;
//    // }

//    return true;
//}

bool RealCurve::loadFromJSON_async(QJsonObject& json) {
    // 用于存储每组异步任务的 future 对象
    std::future<bool> future1;
    std::future<bool> future2;
    std::future<bool> future3;

    // 第一组异步任务
    auto group1Task = [this, &json]() {
        loadFromJsonByCurveType(json);
        loadFromJsonByLayeredType(json);
        loadFromJsonByAverageLabel(json);
        return true;
    };
    future1 = std::async(std::launch::async, group1Task);

    // 第二组异步任务
    auto group2Task = [this, &json]() {
        loadFromJsonByDoseLabels(json);
        loadFromJsonByResterLabels(json);
        loadFromJsonBySputRateType(json);
        return true;
    };
    future2 = std::async(std::launch::async, group2Task);

    // 第三组异步任务
    auto group3Task = [this, &json]() {
        loadFromJsonByRsfType(json);
        loadFromJsonByNorType(json);
        loadFromJsonByMathType(json);
        loadFromJsonByIntraPoints(json);
        loadFromJsonByReducePoints(json);
        loadFromJsonByRcorSims(json);
        loadFromJsonByTruncateType(json);
        loadFromJsonBySmoothType(json);
        loadFromJsonByOffsetType(json);
        loadFromJsonByCleanType(json);
        loadFromJsonByCurveDragType(json);
        loadFromJsonByFinalDragType(json);
        loadFromJsonBySplitConvergeType(json);
        loadFromJsonByCalculations(json);
        loadFromJsonByAvergRsfType(json);
        loadFromJsonByPearkRsfType(json);
        loadFromJsonByDoseRsfType(json);
        loadFromJsonByCorrections(json);
        return true;
    };
    future3 = std::async(std::launch::async, group3Task);

    //等待任务完成;
    future1.get();
    future2.get();
    future3.get();

    return true;
}

 我以为在进行方法时每次都需要创建异步操作去执行,会消耗创建的时间,于是我又设计采用线程池方式,在软件打开时就是用单例模式初始化好一定数量线程池去执行加载方法:
 

/*******************
 * @author 范儿
 * @method
 * @param
 * @description 加载全部数据(异步 线程池)
*******************/
bool RealCurve::loadFromJSON_ThrePol(QJsonObject& json) {
    ThreadPool& pool = ThreadPoolManager::getInstance().getThreadPool();
    std::size_t totalTasks = 8;  // 根据实际添加的任务数量进行设置,这里假设有8个任务

    pool.enqueue([this, &json]() {
        loadFromJsonByCurveType(json);
        loadFromJsonByLayeredType(json);
        loadFromJsonByAverageLabel(json);
        std::lock_guard<std::mutex> guard(mutex);
        completedTasks++;
        cv.notify_one();
    });

    //.................其他保存方法

    pool.enqueue([this, &json]() {
        loadFromJsonByPearkRsfType(json);
        loadFromJsonByDoseRsfType(json);
        loadFromJsonByCorrections(json);
        std::lock_guard<std::mutex> guard(mutex);
        completedTasks++;
        cv.notify_one();
    });

    // 等待所有任务完成
    std::unique_lock<std::mutex> lock(mutex);
    cv.wait(lock, [this, totalTasks]() { return completedTasks >= totalTasks; });

    return true;
}

 于是我拿了一个大量的数据文件对上面几个方法进行测试,结果发现根本没有啥差别嘛,有个别次数同步进行还比异步进行时间要快呢,这不应该呀,难道线程创建/调度/销毁的时间要比读取文件的时间要长? 呃,后面我就不深入的研究什么问题了,可能是每个方法中存储的数据不够大。并且我发现这个读取方法并没有占我总读取方法多少时间。于是我,每步操作我都打印时间出来看哪一步占用的时间较长,如下所示:

/*******************
 * @author 范儿
 * @method loadFromFile
 * @param filePaht 文件路径
 * @description 加载文件数据
*******************/
bool MiddleDocument::loadFromFile2(QString  filePaht){
    QElapsedTimer timer;
    timer.start();

    bool  ret = true;
    // 打开 JSON 文件
    QFile file(filePaht);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Failed to open file for reading.";
        return false;
    }

    double previousReplotTime = timer.nsecsElapsed() * 1e-6;
    qDebug() << "打开文件所用时间" << previousReplotTime;

    // 读取文件内容
    QByteArray jsonData = file.readAll();
    file.close();

    double previousReplotTime2 = timer.nsecsElapsed() * 1e-6;
    qDebug() << "读取文件所有内容所用时间" << previousReplotTime2;

    // 将 JSON 数据解析为 QJsonDocument
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);

    double previousReplotTime3 = timer.nsecsElapsed() * 1e-6;
    qDebug() << "解析转换所用时间" << previousReplotTime3;

    // 检查是否成功解析
    if (jsonDoc.isNull() || !jsonDoc.isObject()) {
        qDebug() << "Invalid JSON document.";
        return false;
    }

    // 获取根 JSON 对象
    QJsonObject jsonObj = jsonDoc.object();

    ret = loadFromJSON(jsonObj);

    double previousReplotTime4 = timer.nsecsElapsed() * 1e-6;
    qDebug() << "读取数据所使用的时间" << previousReplotTime4;
    return ret;
}

 打印结果如下: 这是使用同步方法执行效果

 

 这是使用异步执行效果。

 

 从两张结果分析可以得出结论,读取同步跟异步操作读取文件时间差不多。占总时间也不多,但是解析转换时间占的时间太长了,占了接近80%。 所以应该从这地方进行优化提升。

QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);

 也就是这段代码所使用的时间较长,如何提升,为此我简单问了一下GPT,给出的方案中我选择了使用 Rapidjson第三方库来进行JSON解析,第三方库JSON有多个,根据自己的需求去使用即可。

Rapidjson相对简单容易上手。

RapidJSON 是一个 C++ 的 JSON 解析器及生成器。它的灵感来自 RapidXml
更多介绍、使用教程以及下载地址可访问RapidJSON 官网:http://rapidjson.org/zh-cn

 官方使用说明:

 以下是我转RapidJSON库后的读取代码以及测试结果: 

/*******************
 * @author 范儿
 * @method loadFromFile
 * @param filePaht文件路径
 * @description 加载文件数据
*******************/
bool MiddleDocument::loadFromFile(QString  filePaht){

    QElapsedTimer timer;
    timer.start();
    bool  ret = true;
    // 打开 JSON 文件
    QFile file(filePaht);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Failed to open file for reading.";
        return false;
    }

    // 读取文件内容
    QByteArray jsonData = file.readAll();
    file.close();

    double previousReplotTime2 = timer.nsecsElapsed() * 1e-6;
    qDebug() << "读取文件所有内容所用时间" << previousReplotTime2;

    Document document;
    document.Parse(jsonData.constData());

    double previousReplotTime3 = timer.nsecsElapsed() * 1e-6;
    qDebug() << "数据解析所使用的时间" << previousReplotTime3;

    const rapidjson::Value& rootObject = document.GetObj();
    ret = loadFromRapidJSON(rootObject);

    double previousReplotTime4 = timer.nsecsElapsed() * 1e-6;
    qDebug() << "读取数据所使用的时间" << previousReplotTime4;
    return ret;
}

 相应的代码修改如下所示:很多的方法,我都是直接让GPT给我转的,人工转太麻烦了,转完检查一遍,在测试一下没问题就行。

/**
 * @brief CustomplotImage::loadFromJsonByLeftYAxis
 * @param json
 * @return
 * 加载左Y轴数据
 */
void CustomplotImage::loadFromRapidJSONByLeftYAxis(const Value &newJson) {
    const rapidjson::Value& leftYAxisObj = newJson["leftYAxis"];
    leftYAxisType.isExecute = leftYAxisObj["isExecute"].GetBool();
    leftYAxisType.type = leftYAxisObj["type"].GetInt();
    leftYAxisType.ylower = leftYAxisObj["ylower"].GetDouble();
    leftYAxisType.yupper = leftYAxisObj["yupper"].GetDouble();
    leftYAxisType.isDisplay = leftYAxisObj["isDisplay"].GetBool();
    leftYAxisType.AutoScale = leftYAxisObj["AutoScale"].GetBool();
    leftYAxisType.LogScale = leftYAxisObj["LogScale"].GetBool();
    leftYAxisType.axisLabel = leftYAxisObj["axisLabel"].GetString();
    leftYAxisType.labelColor = leftYAxisObj["labelColor"].GetString();
    leftYAxisType.labelFont = leftYAxisObj["labelFont"].GetString();
    leftYAxisType.labelFormat = leftYAxisObj["labelFormat"].GetString();
}

 测试结果:

 结论:可以明显看到时间明显减少了一半,也就是说RapidJSON比Qt自带的Qt QJsonDocument 要快近一倍。

技术名词解释

1、QJsonObject 和 QJsonDocument(Qt 框架中的 JSON 处理)
  • 数据结构特点
    • QJsonObject:它是 Qt 中用于表示 JSON 对象的类,本质上是一个键值对的容器,其中键是字符串类型,值可以是多种类型(如字符串、数字、布尔值、嵌套的 QJsonObject 或 QJsonArray 等)。例如,{"name": "John", "age": 30}这样的 JSON 对象可以很方便地用 QJsonObject 来表示和操作。
    • QJsonDocument:这是一个更高层次的包装类,用于处理整个 JSON 文档。它可以包含一个 QJsonObject(代表一个 JSON 对象)或者一个 QJsonArray(代表一个 JSON 数组),并且提供了将 JSON 数据转换为字节流(用于存储或网络传输)以及从字节流解析 JSON 数据的功能。
  • 使用场景和优势
    • 集成性好:如果你的项目是基于 Qt 框架开发的,那么 QJsonObject 和 QJsonDocument 与 Qt 的其他组件(如信号槽机制、图形界面相关组件等)能够很好地集成。例如,在 Qt 的模型 - 视图架构中,可以方便地将 QJsonDocument 中的数据展示在视图组件(如 QListView、QTableView 等)中。
    • 简单易用:对于简单的 JSON 数据操作,它们提供了简洁的接口。例如,通过QJsonObject::insert方法可以轻松地插入新的键值对,QJsonDocument::fromJson方法可以方便地从字节数组中解析 JSON 数据。
    • 序列化和反序列化方便:QJsonDocument 提供了toJson方法,可以将 JSON 数据转换为字节数组(可以指定格式,如紧凑格式或缩进格式),便于存储或传输。同时,从字节数组反序列化 JSON 数据也很简单,这使得在处理网络通信或者本地文件存储中的 JSON 数据时非常方便。。
  • 局限性
    • 性能方面:在处理大规模或高性能要求的 JSON 数据处理场景下,QJsonObject 和 QJsonDocument 可能不如一些专门的 JSON 处理库高效。因为 Qt 的 JSON 处理功能在设计时可能更侧重于易用性和与框架的集成性,而不是极致的性能优化。
    • 功能深度:对于一些复杂的 JSON Schema 验证或者高级的 JSON 操作(如复杂的 JSON 指针操作等),Qt 的原生 JSON 处理类可能支持不够完善。
2、rapidjson 和 document(rapidjson 库中的 JSON 处理)
  • 数据结构特点
    • rapidjson::Document:在 rapidjson 中,Document 是核心的数据结构,它可以表示整个 JSON 文档。它有两种基本的类型,一种是对象(类似于 QJsonObject 中的键值对集合),另一种是数组。它内部使用了高效的内存管理和解析算法来处理 JSON 数据。例如,一个包含多个人员信息的 JSON 数组可以通过 rapidjson::Document 来解析和操作。
    • 其他数据类型:rapidjson 还提供了诸如 Value(用于表示 JSON 中的各种值,如字符串、数字、布尔值等)、GenericValue(更通用的 JSON 值表示,支持不同的编码等)、GenericArray(用于表示 JSON 数组)等类型,这些类型一起构成了完整的 JSON 数据处理体系。
  • 使用场景和优势
    • 高性能:rapidjson 是一个以性能为导向设计的 JSON 解析库,它在解析和序列化 JSON 数据时速度非常快,适用于对性能要求较高的场景,如服务器端的 JSON 数据处理、大数据量的 JSON 数据转换等。
    • 内存管理高效:它采用了自定义的内存分配器,能够有效减少内存碎片,提高内存使用效率。例如,在处理大量小 JSON 对象的场景下,这种内存管理方式的优势更加明显。
    • 灵活性:提供了丰富的接口,可以对 JSON 数据进行精细的操作。比如可以直接通过指针操作 JSON 数据结构中的元素,对于高级用户来说,可以实现一些复杂的 JSON 数据转换和处理逻辑。
  • 局限性
    • 学习曲线较陡:相比 Qt 的 JSON 处理类,rapidjson 的接口更加复杂,需要一定的时间来学习和掌握。例如,正确地管理内存分配器和理解各种 Value 类型的操作方法需要花费一些精力。
    • 与框架集成性差:如果你的项目是基于 Qt 等特定框架开发的,rapidjson 与这些框架的集成可能不如框架原生的 JSON 处理类方便。它没有像 Qt 的信号槽机制这样的集成功能,在与其他框架组件交互时可能需要额外的代码来进行适配

小结

        以上就是我在开发Qt软件对文件进行JSON序列化和反序列化的过程,工具的使用有利也有弊,根据自己的项目需求和长远规划来做设计,就兼容性而言Qt自带的工具肯定要比三方库兼容性要高更多合适的方法,但第三方库针对某一功能而言性能就高的多,最终根据自己项目规划取舍。
温馨提示: 当前软件开发没有开源,即没有Demo提供参考,直接使用实战的代码进行的总结,所以不好分享,请谅解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值