簡述
Qt5 中包含了處理 JSON 的類,均以 QJson 開頭(例如:QJsonDocument、QJsonArray、QJsonObject),在 QtCore 模塊中,不需要額外引入其它模塊。
常用的 JSON 庫
json.org 中介紹了 JSON 在各種語言中的應用,在 C/C++ 中比較常用的JSON 庫主要有以下幾個:
-
JsonCpp
JsonCpp 是一個 C++ 用來處理 JSON 數據的開發包。
網址:http://jsoncpp.sourceforge.net/ -
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 常用類
QJsonDocument
QJsonDocument 類用於讀和寫 JSON 文檔。
一個 JSON 文檔可以使用 QJsonDocument::fromJson() 從基於文本的表示轉化為 QJsonDocument, toJson() 則可以反向轉化為文本。解析器非常快且高效,並將 JSON 轉換為 Qt 使用的二進制表示。
已解析文檔的有效性,可以使用 !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" }
- 1
- 2
- 3
- 4
- 5
生成比較簡單,由於是一個對象,只需要用 QJsonObject 即可。
// 構建 JSON 對象
QJsonObject json; json.insert("Name", "Qt"); json.insert("From", 1991); json.insert("Cross Platform", true); // 構建 JSON 文檔 QJsonDocument document; document.setObject(json); QByteArray byteArray = document.toJson(QJsonDocument::Compact); QString strJson(byteArray); qDebug() << strJson;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
解析如下:
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; } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
注意:在轉化為 QJsonDocument 后,首先需要根據 QJsonParseError 的值判定是否轉化成功,然后在進行相應的轉化解析。
簡單的 JSON 數組
構造一個簡單的 JSON 對象:
[
"Qt", 5.7, true ]
- 1
- 2
- 3
- 4
- 5
生成比較簡單,由於是一個數組,只需要用 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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
需要注意的是,和上面不同的是,這里使用的是 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; } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
和 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 ] }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
包含了一個擁有五個“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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
解析部分其實並沒有看起來這么復雜,只要一步步搞明白對應的類型,然后進行相應轉化即可。
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; } } } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
基本的用法就這些,比較簡單,細節很關鍵,建議在處理的過程中啟用嚴格模式,例如:先通過 QJsonParseError::NoError 判斷轉化 JSON 文檔無誤,再進行解析。在解析過程中,先判斷 QJsonValue 是否為對應的類型如 isObject(),再通過 toObject() 轉化。