Qt之JSON生成與解析
簡述
JSON(JavaScript Object Notation)是一種輕量級的資料交換格式。它基於 JavaScript 的一個子集。 JSON 採用完全獨立於語言的文字格式,但是也使用了類似於 C 語言家族的習慣(包括 C、C++、C#、Java、JavaScript、Perl、Python 等)。這些特性使 JSON 成為理想的資料交換語言。易於人閱讀和編寫,同時也易於機器解析和生成。關於 JSON 字串的詳細介紹,可以參考:JSON簡介
常用的JSON庫
json.org 中介紹了 JSON 在各種語言中的應用,在 C/C++ 中比較常用的 JSON 庫主要有以下幾個:
- JsonCpp
JsonCpp 是一個 C++ 用來處理 JSON 資料的開發包。
網址: - cJSON
cJSON 是一個超輕巧,攜帶方便,單檔案,簡單的可以作為 ANSI-C 標準的 JSON 解析器。
網址:http://sourceforge.net/projects/cjson/ - QJson
QJson 是一個基於 Qt 的開發包用來將 JSON 資料解析成 QVariant 物件,JSON 的陣列將被對映為QVariantList 例項,而其他物件對映為 QVariantMap 例項。
網址:http://qjson.sourceforge.net/
關於 Qt 中對 JSON 的生成與解析,Qt5 以前的版本,可以使用 QJson 庫,需要單獨下載、編譯,才能使用。到了 Qt5,提供了專門的 QJsonDocument 及其相關類來讀和寫 JSON 文件。
JSON 常用類
首先,JSON 的解析和構建都要包含如下標頭檔案,下面一個個介紹下:
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
QJsonDocument
QJsonDocument 類用於讀和寫 JSON 文件。
一個 JSON 文件可以使用 QJsonDocument::fromJson() 從基於文字的表示轉化為 QJsonDocument, toJson(JsonFormat format = Indented) 則可以反向轉化為文字,其中,format主要有兩種格式,一種是人們可讀的格式,一種是緊湊的格式。
已解析文件的有效性,可以使用 !isNull() 進行查詢。
如果要查詢一個 JSON 文件是否包含一個數組或一個物件,使用 isArray() 和 isObject()。包含在文件中的陣列或物件可以使用 array() 或 object() 檢索,然後讀取或操作。
也可以使用 fromBinaryData() 或 fromRawData() 從儲存的二進位制表示建立來 JSON 文件。
QJsonArray
QJsonArray 類封裝了一個 JSON 陣列。
JSON 陣列是值的列表。列表可以被操作,通過從陣列中插入和刪除 QJsonValue 。
一個 QJsonArray 可以和一個 QVariantList 相互轉換。可以使用 size() 來查詢條目的數量,通過 insert() 在指定索引處插入值,removeAt() 來刪除指定索引的值。
QJsonObject
QJsonObject 類封裝了一個 JSON 物件。
一個 JSON 物件是一個 “key/value” 列表,key 是獨一無二的字串,value 由一個 QJsonValue 表示。
一個 QJsonObject 可以和一個 QVariantMap 相互轉換。可以使用 size() 來查詢“key/value 對”的數量,通過 insert() 插入“key/value 對”, remove() 刪除指定的 key。
QJsonValue
QJsonValue 類封裝了一個值。JSON 中的值有 6 種基本資料型別:
- bool(QJsonValue::Bool)
- double(QJsonValue::Double)
- string(QJsonValue::String)
- array(QJsonValue::Array)
- object(QJsonValue::Object)
- null(QJsonValue::Null)
一個值可以由任何上述資料型別表示。此外,QJsonValue 有一個特殊的標記來表示未定義的值,可以使用 isUndefined() 查詢。
值的型別可以通過 type() 或 isBool()、isString() 等訪問函式查詢。同樣地,值可以通過 toBool()、toString() 等函式轉化成相應的儲存型別。
QJsonParseError
QJsonParseError 類用於在 JSON 解析中報告錯誤。
列舉 QJsonParseError::ParseError:該列舉描述 JSON 文件在解析過程中所發生的錯誤型別。
常量 | 值 | 描述 |
---|---|---|
QJsonParseError::NoError | 0 | 未發生錯誤 |
QJsonParseError::UnterminatedObject | 1 | 物件不正確地終止以右花括號結束 |
QJsonParseError::MissingNameSeparator | 2 | 分隔不同項的逗號丟失 |
QJsonParseError::UnterminatedArray | 3 | 陣列不正確地終止以右中括號結束 |
QJsonParseError::MissingValueSeparator | 4 | 物件中分割 key/value 的冒號丟失 |
QJsonParseError::IllegalValue | 5 | 值是非法的 |
QJsonParseError::TerminationByNumber | 6 | 在解析數字時,輸入流結束 |
QJsonParseError::IllegalNumber | 7 | 數字格式不正確 |
QJsonParseError::IllegalEscapeSequence | 8 | 在輸入時,發生一個非法轉義序列 |
QJsonParseError::IllegalUTF8String | 9 | 在輸入時,發生一個非法 UTF8 序列 |
QJsonParseError::UnterminatedString | 10 | 字串不是以引號結束 |
QJsonParseError::MissingObject | 11 | 一個物件是預期的,但是不能被發現 |
QJsonParseError::DeepNesting | 12 | 對解析器來說,JSON 文件巢狀太深 |
QJsonParseError::DocumentTooLarge | 13 | 對解析器來說,JSON 文件太大 |
QJsonParseError::GarbageAtEnd | 14 | 解析的文件在末尾處包含額外的亂碼 |
JSON 物件的構建與解析
一個簡單的 JSON 物件:
{
"Cross Platform": true,
"From": 1991,
"Name": "Qt"
}
構建比較簡單,由於是一個物件,只需要用 QJsonObject 即可:
// 構建 JSON 物件
QJsonObject jsonObj;
jsonObj.insert("Name", "Qt");
jsonObj.insert("From", 1991);
jsonObj.insert("Cross Platform", true);
// 構建 JSON 文件
QJsonDocument document;
document.setObject(jsonObj);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
qDebug() << strJson;
解析如下:
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(byteArray, &jsonError); // 轉化為 JSON 文件
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未發生錯誤
if (doucment.isObject()) { // JSON 文件為物件
QJsonObject object = doucment.object(); // 轉化為物件
if (object.contains("Name")) { // 包含指定的 key
QJsonValue value = object.value("Name"); // 獲取指定 key 對應的 value
if (value.isString()) { // 判斷 value 是否為字串
QString strName = value.toString(); // 將 value 轉化為字串
qDebug() << "Name : " << strName;
}
}
if (object.contains("From")) {
QJsonValue value = object.value("From");
if (value.isDouble()) {
int nFrom = value.toVariant().toInt();
qDebug() << "From : " << nFrom;
}
}
if (object.contains("Cross Platform")) {
QJsonValue value = object.value("Cross Platform");
if (value.isBool()) {
bool bCrossPlatform = value.toBool();
qDebug() << "CrossPlatform : " << bCrossPlatform;
}
}
}
}
注意:在轉化為 QJsonDocument 後,首先需要根據 QJsonParseError 的值判定是否轉化成功,然後在進行相應的轉化解析。
JSON 陣列的構建與解析
一個簡單的 JSON 陣列:
[
"Qt",
5.7,
true
]
生成比較簡單,由於是一個數組,只需要用 QJsonArray 即可:
// 構建 JSON 陣列
QJsonArray json;
json.append("Qt");
json.append(5.7);
json.append(true);
// 構建 JSON 文件
QJsonDocument document;
document.setArray(json);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
qDebug() << strJson;
需要注意的是,和上面不同的是,這裡使用的是 QJsonDocument 的 setArray() 函式,因為是陣列嘛!
解析如下:
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(byteArray, &jsonError); // 轉化為 JSON 文件
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未發生錯誤
if (doucment.isArray()) { // JSON 文件為陣列
QJsonArray array = doucment.array(); // 轉化為陣列
int nSize = array.size(); // 獲取陣列大小
for (int i = 0; i < nSize; ++i) { // 遍歷陣列
QJsonValue value = array.at(i);
if (value.type() == QJsonValue::String) {
QString strName = value.toString();
qDebug() << strName;
}
if (value.type() == QJsonValue::Double) {
double dVersion = value.toDouble();
qDebug() << dVersion;
}
if (value.type() == QJsonValue::Bool) {
bool bCrossPlatform = value.toBool();
qDebug() << bCrossPlatform;
}
}
}
}
和 JSON 物件類似,在遍歷陣列時,獲取每個 value,首先需要判斷 value 的型別(和 is***() 函式類似,這裡根據 type() 函式返回的列舉值來判斷),然後再進行相應的轉換。
複雜的JSON的構建與解析
構造一個複雜的 JSON 物件:
{
"Company": "Digia",
"From": 1991,
"Name": "Qt",
"Page": {
"Developers": "https://www.qt.io/developers/",
"Download": "https://www.qt.io/download/",
"Home": "https://www.qt.io/"
},
"Version": [
4.8,
5.2,
5.7
]
}
包含了一個擁有五個 “key/value” 的物件,values 中的兩個(Company、Name)是字串,一個(From)是數字,一個(Page)是物件,一個(Version)是陣列。
要生成這樣一個複雜的 JSON 文件,需要分別構造物件和陣列,然後將它們拼接起來:
// 構建 Json 陣列 - Version
QJsonArray versionArray;
versionArray.append(4.8);
versionArray.append(5.2);
versionArray.append(5.7);
// 構建 Json 物件 - Page
QJsonObject pageObject;
pageObject.insert("Home", "https://www.qt.io/");
pageObject.insert("Download", "https://www.qt.io/download/");
pageObject.insert("Developers", "https://www.qt.io/developers/");
// 構建 Json 物件
QJsonObject json;
json.insert("Name", "Qt");
json.insert("Company", "Digia");
json.insert("From", 1991);
json.insert("Version", QJsonValue(versionArray));
json.insert("Page", QJsonValue(pageObject));
// 構建 Json 文件
QJsonDocument document;
document.setObject(json);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
qDebug() << strJson;
解析部分其實並沒有看起來這麼複雜,只要一步步搞明白對應的型別,然後進行相應轉化即可。
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(byteArray, &jsonError); // 轉化為 JSON 文件
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未發生錯誤
if (doucment.isObject()) { // JSON 文件為物件
QJsonObject object = doucment.object(); // 轉化為物件
if (object.contains("Name")) {
QJsonValue value = object.value("Name");
if (value.isString()) {
QString strName = value.toString();
qDebug() << "Name : " << strName;
}
}
if (object.contains("Company")) {
QJsonValue value = object.value("Company");
if (value.isString()) {
QString strCompany = value.toString();
qDebug() << "Company : " << strCompany;
}
}
if (object.contains("From")) {
QJsonValue value = object.value("From");
if (value.isDouble()) {
int nFrom = value.toVariant().toInt();
qDebug() << "From : " << nFrom;
}
}
if (object.contains("Version")) {
QJsonValue value = object.value("Version");
if (value.isArray()) { // Version 的 value 是陣列
QJsonArray array = value.toArray();
int nSize = array.size();
for (int i = 0; i < nSize; ++i) {
QJsonValue value = array.at(i);
if (value.isDouble()) {
double dVersion = value.toDouble();
qDebug() << "Version : " << dVersion;
}
}
}
}
if (object.contains("Page")) {
QJsonValue value = object.value("Page");
if (value.isObject()) { // Page 的 value 是物件
QJsonObject obj = value.toObject();
if (obj.contains("Home")) {
QJsonValue value = obj.value("Home");
if (value.isString()) {
QString strHome = value.toString();
qDebug() << "Home : " << strHome;
}
}
if (obj.contains("Download")) {
QJsonValue value = obj.value("Download");
if (value.isString()) {
QString strDownload = value.toString();
qDebug() << "Download : " << strDownload;
}
}
if (obj.contains("Developers")) {
QJsonValue value = obj.value("Developers");
if (value.isString()) {
QString strDevelopers = value.toString();
qDebug() << "Developers : " << strDevelopers;
}
}
}
}
}
}
基本的用法就這些,比較簡單,細節很關鍵,建議在處理的過程中啟用嚴格模式,例如:先通過 QJsonParseError::NoError 判斷轉化 JSON 文件無誤,再進行解析。在解析過程中,先判斷 QJsonValue 是否為對應的型別如 isObject(),再通過 toObject() 轉化。
參考: