Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程序,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹如何運用QJson
組件的實現對JSON文本的靈活解析功能。
JSON(JavaScript Object Notation)是一種輕量級的數據交換格式,它易於人閱讀和編寫,也易於機器解析和生成。該格式是基於JavaScript語言的一個子集,但它是一種獨立於語言的數據格式,因此可以在許多不同的編程語言中使用。
該數據是以鍵值對的形式組織的,其中鍵是字符串,值可以是字符串、數字、布爾值、數組、對象(即嵌套的鍵值對集合)或null,在Qt中默認提供了QJson
系列類庫,使用該類庫可以很方便的解析和處理JSON文檔。
1.1 解析單一鍵值對
實現解析根中的單一鍵值對,例如解析config.json
配置文件中的blog,enable,status
等這些獨立的字段值,在解析之前需要先通過QJsonDocument::fromJson
將內存中的字符串格式化爲QJsonDocument
類型,當有着該類型之後,則我們可以使用*.object()
將其轉換爲對應的QJsonObject
對象,在對象中我們可以調用各種方法對內存中的JSON
數據進行處理。
以下是關於 QJsonDocument
類的一些常用方法的說明:
方法 | 說明 |
---|---|
QJsonDocument() |
構造函數,創建一個空的 JSON 文檔。 |
QJsonDocument(const QJsonObject &object) |
通過給定的 JSON 對象構造 JSON 文檔。 |
QJsonDocument(const QJsonArray &array) |
通過給定的 JSON 數組構造 JSON 文檔。 |
QJsonDocument(const QJsonValue &value) |
通過給定的 JSON 值構造 JSON 文檔。 |
QJsonDocument(const QJsonDocument &other) |
複製構造函數。 |
QJsonDocument &operator=(const QJsonDocument &other) |
賦值運算符。 |
bool isNull() const |
檢查文檔是否爲空。 |
bool isEmpty() const |
檢查文檔是否爲空,包括 JSON 數組或對象爲空的情況。 |
QJsonObject object() const |
返回文檔中的 JSON 對象。 |
QJsonArray array() const |
返回文檔中的 JSON 數組。 |
QJsonValue value() const |
返回文檔中的 JSON 值。 |
bool isArray() const |
檢查文檔是否包含 JSON 數組。 |
bool isObject() const |
檢查文檔是否包含 JSON 對象。 |
QByteArray toBinaryData() const |
將文檔轉換爲二進制數據。 |
bool fromBinaryData(const QByteArray &data) |
從二進制數據恢復文檔。 |
QString toJson(QJsonDocument::JsonFormat format = QJsonDocument::Compact) const |
返回 JSON 字符串表示,可以選擇格式化的方式。 |
static QJsonDocument fromJson(const QString &json, QJsonParseError *error = nullptr) |
從 JSON 字符串創建文檔。 |
以下是關於 QJsonObject
類的一些常用方法的說明:
方法 | 說明 |
---|---|
QJsonObject() |
構造函數,創建一個空的 JSON 對象。 |
QJsonObject(const QJsonObject &other) |
複製構造函數。 |
QJsonObject &operator=(const QJsonObject &other) |
賦值運算符。 |
bool isEmpty() const |
檢查對象是否爲空。 |
int size() const |
返回對象中鍵值對的數量。 |
bool contains(const QString &key) const |
檢查對象中是否包含指定的鍵。 |
QStringList keys() const |
返回對象中所有鍵的列表。 |
QJsonValue value(const QString &key) const |
返回與指定鍵關聯的值。 |
void insert(const QString &key, const QJsonValue &value) |
向對象中插入鍵值對。 |
QJsonObject &unite(const QJsonObject &other) |
將另一個對象的鍵值對合併到當前對象。 |
void remove(const QString &key) |
從對象中移除指定鍵及其關聯的值。 |
QJsonValue take(const QString &key) |
移除並返回與指定鍵關聯的值。 |
void clear() |
移除對象中的所有鍵值對,使其變爲空對象。 |
QJsonDocument toDocument() const |
將對象轉換爲 JSON 文檔。 |
當需要讀取參數時只需要使用find()
方法查詢特定字段中的key值即可,按鈕on_pushButton_clicked
被點擊後執行如下流程;
void MainWindow::on_pushButton_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 解析blog字段
QString blog = root_object.find("blog").value().toString();
//std::cout << "字段對應的值 = > "<< blog.toStdString() << std::endl;
ui->lineEdit_blog->setText(blog);
// 解析enable字段
bool enable = root_object.find("enable").value().toBool();
//std::cout << "是否開啓狀態: " << enable << std::endl;
ui->lineEdit_enable->setText(QString::number(enable));
// 解析status字段
int status = root_object.find("status").value().toInt();
//std::cout << "狀態數值: " << status << std::endl;
ui->lineEdit_status->setText(QString::number(status));
}
運行後點擊讀取數據按鈕,輸出效果如下;
1.2 解析單數組鍵值
實現解析簡單的單一對象與單一數組結構,如配置文件中的GetDict
與GetList
既是我們需要解析的內容,在解析時我們需要通過toVariantMap
將字符串轉換爲對應的Map容器,當數據被轉換後則就可以通過Map[]
的方式很容易的將其提取出來。
void MainWindow::on_pushButton_2_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 解析單一對象
QJsonObject get_dict_ptr = root_object.find("GetDict").value().toObject();
QVariantMap map = get_dict_ptr.toVariantMap();
if(map.contains("address") && map.contains("username") && map.contains("password") && map.contains("update"))
{
QString address = map["address"].toString();
QString username = map["username"].toString();
QString password = map["password"].toString();
QString update = map["update"].toString();
std::cout
<< " 地址: " << address.toStdString()
<< " 用戶名: " << username.toStdString()
<< " 密碼: " << password.toStdString()
<< " 更新日期: " << update.toStdString()
<< std::endl;
ui->listWidget->addItem(address);
ui->listWidget->addItem(username);
ui->listWidget->addItem(password);
ui->listWidget->addItem(update);
}
// 解析單一數組
QJsonArray get_list_ptr = root_object.find("GetList").value().toArray();
for(int index=0; index < get_list_ptr.count(); index++)
{
int ref_value = get_list_ptr.at(index).toInt();
std::cout << "輸出數組元素: " << ref_value << std::endl;
ui->listWidget_2->addItem(QString::number(ref_value));
}
}
運行後點擊解析數據按鈕,輸出效果如下;
1.3 解析多數組鍵值
實現解析字典嵌套字典或字典嵌套數組的結構,如配置文件中的ObjectInArrayJson
則是一個字典中嵌套了另外兩個字典而每個字典中的值又是一個Value
數組,而與之相對應的ArrayJson
則是在列表中嵌套了另外一個列表,這兩中結構的使用讀者可參照如下案例;
首先我們來看ObjectInArrayJson
是如何被解析的,我們分別準備兩個ComboBox
選擇框,當讀者點擊按鈕時我們通過toVariantMap
將字典轉換爲一個MAP容器,並通過toJsonArray
轉換內部的列表到JsonArray
容器內,其初始化部分如下所示;
void MainWindow::on_pushButton_3_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 找到Object對象
QJsonObject one_object_json = root_object.find("ObjectInArrayJson").value().toObject();
// 轉爲MAP映射
QVariantMap map = one_object_json.toVariantMap();
// 尋找One鍵
QJsonArray array_one = map["One"].toJsonArray();
for(int index=0; index < array_one.count(); index++)
{
QString value = array_one.at(index).toString();
std::cout << "One => "<< value.toStdString() << std::endl;
ui->comboBox->addItem(value);
}
// 尋找Two鍵
QJsonArray array_two = map["Two"].toJsonArray();
for(int index=0; index < array_two.count(); index++)
{
QString value = array_two.at(index).toString();
std::cout << "Two => "<< value.toStdString() << std::endl;
ui->comboBox_2->addItem(value);
}
}
同理,要實現解析數組中的數組也可以通過該方式實現,如配置文件中的ArrayJson
既是我們需要解析的內容,首先我們通過isArray
判斷該節點是否爲數組,如果是則通過toArray().at
方法以此得到不同下標元素參數,並依次循環即可,其代碼如下所示;
void MainWindow::on_pushButton_4_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 獲取MyJson數組
QJsonValue array_value = root_object.value("ArrayJson");
// 驗證節點是否爲數組
if(array_value.isArray())
{
// 得到數組個數
int array_count = array_value.toArray().count();
// 循環數組個數
for(int index=0;index <= array_count;index++)
{
QJsonValue parset = array_value.toArray().at((index));
if(parset.isArray())
{
QString address = parset.toArray().at(0).toString();
QString username = parset.toArray().at(1).toString();
QString userport = parset.toArray().at(2).toString();
std::cout
<< "地址: " << address.toStdString()
<< "用戶名: " << username.toStdString()
<< "端口號: " << userport.toStdString()
<< std::endl;
ui->comboBox_3->addItem(address);
ui->comboBox_4->addItem(username);
ui->comboBox_5->addItem(userport);
}
}
}
}
運行後點擊兩個初始化按鈕則可以將字典或列表中的數據依次解析到不同的ComBobox
列表框內,輸出效果如下;
1.4 解析多字典鍵值
實現解析字典中嵌套多個參數或字典中嵌套參數中包含列表的數據集,如配置文件中的ObjectJson
則是字典中存在多個鍵值對,而ObjectArrayJson
則更進一步在多鍵值對中增加了列表的支持,解析此類內容只需要依次逐級拆分即可,我們來看下如何實現對這些鍵值的靈活提取;
首先我們來實現對ObjectJson
的參數解析功能,讀者可自行對比與之前1.3
中的區別,可以發現這兩者的差別其實不大,解析ObjectJson
完整代碼如下所示;
void MainWindow::on_pushButton_5_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 獲取MyJson數組
QJsonValue object_value = root_object.value("ObjectJson");
// 驗證是否爲數組
if(object_value.isArray())
{
// 獲取對象個數
int object_count = object_value.toArray().count();
// 循環個數
for(int index=0;index <= object_count;index++)
{
QJsonObject obj = object_value.toArray().at(index).toObject();
// 驗證數組不爲空
if(!obj.isEmpty())
{
QString address = obj.value("address").toString();
QString username = obj.value("username").toString();
std::cout << "地址: " << address.toStdString() << " 用戶: " << username.toStdString() << std::endl;
ui->comboBox_6->addItem(address);
ui->comboBox_7->addItem(username);
}
}
}
}
接着我們來實現一個更爲複雜的需求,解析多字典中嵌套的數組,如配置文件中的ObjectArrayJson
則是我們需要解析的內容,在之前解析字典部分保持與上述案例一致,唯一不同的是我們需要通過value("ulist").toArray()
獲取到對應字典中的數組,並通過循環的方式輸出。
如下案例中,當讀者點擊初始化按鈕時我們首先讓字典中的數據填充之ComboBox
列表框中,接着當讀者點擊第一個列表框時我們讓其過濾出特定的內容並賦值到第二個列表框中,以此實現聯動效果,首先初始化部分如下所示;
void MainWindow::on_pushButton_6_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 獲取MyJson數組
QJsonValue object_value = root_object.value("ObjectArrayJson");
// 驗證是否爲數組
if(object_value.isArray())
{
// 獲取對象個數
int object_count = object_value.toArray().count();
// 循環個數
for(int index=0;index <= object_count;index++)
{
QJsonObject obj = object_value.toArray().at(index).toObject();
// 驗證數組不爲空
if(!obj.isEmpty())
{
QString uname = obj.value("uname").toString();
std::cout << "用戶名: " << uname.toStdString() << std::endl;
ui->comboBox_8->addItem(uname);
// 解析該用戶的數組
int array_count = obj.value("ulist").toArray().count();
std::cout << "數組個數: "<< array_count << std::endl;
for(int index=0;index < array_count;index++)
{
QJsonValue parset = obj.value("ulist").toArray().at(index);
int val = parset.toInt();
std::cout << "Value = > "<< val << std::endl;
ui->comboBox_9->addItem(QString::number(val));
}
}
}
}
}
當第一個選擇框被選中時我們觸發currentIndexChanged
信號,在其中只需要判斷uname.compare(arg1)
是否相等如果相等則addItem
追加到新的列表內,運行效果如下所示,詳細實現可參考附件。
1.5 解析多字典嵌套
實現解析多個字典嵌套或多個列表嵌套的結構,如配置文件中的NestingObjectJson
則是字典中嵌套字典,而ArrayNestingArrayJson
則是列表中嵌套列表,兩種的解析方式基本一致。
我們首先來實現第一種格式的解析,當按鈕被點擊後,我們首先查詢uuid
字段並賦值到ComBobox
列表中,實現代碼如下所示;
void MainWindow::on_pushButton_7_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 獲取NestingObjectJson數組
QJsonValue array_value = root_object.value("NestingObjectJson");
// 驗證節點是否爲數組
if(array_value.isArray())
{
// 得到內部對象個數
int count = array_value.toArray().count();
std::cout << "對象個數: " << count << std::endl;
for(int index=0; index < count; index++)
{
// 得到數組中的index下標中的對象
QJsonObject array_object = array_value.toArray().at(index).toObject();
QString uuid = array_object.value("uuid").toString();
// 追加數據
std::cout << uuid.toStdString() << std::endl;
ui->comboBox_10->addItem(uuid);
// 開始解析basic中的數據
QJsonObject basic = array_object.value("basic").toObject();
QString lat = basic.value("lat").toString();
QString lon = basic.value("lon").toString();
std::cout << "解析basic中的lat字段: " << lat.toStdString() << std::endl;
std::cout << "解析basic中的lon字段: " << lon.toStdString()<< std::endl;
// 解析單獨字段
QString status = array_object.value("status").toString();
std::cout << "解析字段狀態: " << status.toStdString() << std::endl;
}
}
}
當ComBobox
組件中的currentIndexChanged
信號被觸發時,則直接執行對LineEdit
編輯框的賦值操作,其代碼如下所示;
void MainWindow::on_comboBox_10_currentIndexChanged(const QString &arg1)
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::information(nullptr,"提示","JSON格式錯誤",QMessageBox::Ok);
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 獲取NestingObjectJson數組
QJsonValue array_value = root_object.value("NestingObjectJson");
// 驗證節點是否爲數組
if(array_value.isArray())
{
// 得到內部對象個數
int count = array_value.toArray().count();
std::cout << "對象個數: " << count << std::endl;
for(int index=0; index < count; index++)
{
// 得到數組中的index下標中的對象
QJsonObject array_object = array_value.toArray().at(index).toObject();
QString uuid = array_object.value("uuid").toString();
// 對比是否相等
if(uuid.compare(arg1) == 0)
{
// 開始解析basic中的數據
QJsonObject basic = array_object.value("basic").toObject();
QString lat = basic.value("lat").toString();
QString lon = basic.value("lon").toString();
std::cout << "解析basic中的lat字段: " << lat.toStdString() << std::endl;
std::cout << "解析basic中的lon字段: " << lon.toStdString()<< std::endl;
ui->lineEdit->setText(lat);
ui->lineEdit_2->setText(lon);
// 解析單獨字段
QString status = array_object.value("status").toString();
std::cout << "解析字段狀態: " << status.toStdString() << std::endl;
}
}
}
}
同理,我們也可以實現字典中嵌套列表結構,如配置文件中的ArrayNestingArrayJson
既我們需要解析的內容,解析實現方法與上述代碼保持一致,首先當按鈕被點擊後我們直接對ComBobox
組件進行初始化,代碼如下所示;
void MainWindow::on_pushButton_8_clicked()
{
// 字符串格式化爲JSON
QJsonParseError err_rpt;
QJsonDocument root_document = QJsonDocument::fromJson(config.toUtf8(), &err_rpt);
if(err_rpt.error != QJsonParseError::NoError)
{
std::cout << "json 格式錯誤" << std::endl;
}
// 獲取到Json字符串的根節點
QJsonObject root_object = root_document.object();
// 獲取NestingObjectJson數組
QJsonValue array_value = root_object.value("ArrayNestingArrayJson");
// 驗證節點是否爲數組
if(array_value.isArray())
{
// 得到數組中的0號下標中的對象
QJsonObject array_object = array_value.toArray().at(0).toObject();
// 解析手機號字符串
QString telephone = array_object.value("telephone").toString();
std::cout << "手機號: " << telephone.toStdString() << std::endl;
ui->comboBox_11->addItem(telephone);
// 定位外層數組
QJsonArray root_array = array_object.find("path").value().toArray();
std::cout << "外層循環計數: " << root_array.count() << std::endl;
for(int index=0; index < root_array.count(); index++)
{
// 定位內層數組
QJsonArray sub_array = root_array.at(index).toArray();
std::cout << "內層循環計數: "<< sub_array.count() << std::endl;
for(int sub_count=0; sub_count < sub_array.count(); sub_count++)
{
// 每次取出最裏層數組元素
float var = sub_array.toVariantList().at(sub_count).toFloat();
std::cout << "輸出元素: " << var << std::endl;
// std::cout << sub_array.toVariantList().at(0).toFloat() << std::endl;
ui->listWidget_3->addItem(QString::number(var));
}
}
}
}
運行效果如下圖所示;