當前位置:成語大全網 - 新華字典 - 如何在 Swift 中優雅地處理 JSON

如何在 Swift 中優雅地處理 JSON

JSON數據轉換

Perfect通過壹系列Swift自建數據類型的擴展實現了基本的JSON編碼和解碼工具。解碼是通過在Swift字符串類型基礎上實現的擴展。

Perfect 的JSON函數庫是開源的,請查看這裏下載並安裝Perfect對Swift JSON的支持:

/PerfectlySoft/Perfect/

請註意雖然Perfect的JSON工具功能強大,但對您的系統而言不是必須的,請根據需要自行選擇引用該工具庫內的函數。

如果需要使用本系統,請首先在您的源代碼開始部分確保PerfectLib庫函數已經聲明導入:

import PerfectLib

將數據編碼為JSON格式

您可以將以下數據類型直接轉換為JSON字符串:

String 字符串

Int 整型

UInt 無符號整型

Double 雙精度浮點型

Bool 布爾型

Array 任意類型的數組

Dictionary 以字符串為關鍵詞的字典

Optional 可選類型

從JSONConvertibleObject對象繼承而來的定制類型

註意?對於可選類型而言,只有包含上述任意壹種類型的Optional類型才是可以直接轉換的。對於值為nil的Optionals類型來說,JSON字符串輸出結果將會是"null"。

為了實現上述變量類型的編碼,請調用上述對象的jsonEncodedString()函數。這個函數是Perfect專門做的擴展。該函數有可能會拋出JSONConversionError.notConvertible無法轉換的異常。

舉例

let scoreArray: [String:Any] = ["第壹名": 300, "第二名": 230.45, "第三名": 150]

let encoded = try scoreArray.jsonEncodedString()

編碼結果是如下字符串:

{"第二名":230.45,"第壹名":300,"第三名":150}

解碼JSON數據

包含JSON格式數據的字符串可以用jsonDecode()函數解碼。如果格式有問題,該函數會拋出JSONConversionError.syntaxError語法錯誤異常。

let encoded = "{\"第二名\":230.45,\"第壹名\":300,\"第三名\":150}"

let decoded = try encoded.jsonDecode() as? [String:Any]

對上述字符串的解碼將會生成下列內容的字典類型:

["第二名": 230.44999999999999, "第壹名": 300, "第三名": 150]

由於解碼JSON字符串可能產生任意數據值,因此最常見的方法是用JSON對象(字典)或者數組進行處理。您需要根據結果自行按照預期類型進行轉換。

解碼後的數據使用

因為解碼後的結果總是[String:Any]字典或者[Any]數組,因此您需要其包含的數據轉換為預期類型,比如:

var firstPlace = 0

var secondPlace = 0.0

var thirdPlace = 0

let encoded = "{\"第二名\":230.45,\"第壹名\":300,\"第三名\":150}"

guard let decoded = try encoded.jsonDecode() as? [String:Any] else {

return

}

for (key, value) in decoded {

switch key {

case "第壹名":

firstPlace = value as! Int

case "第二名":

secondPlace = value as! Double

case "第三名":

thirdPlace = value as! Int

default:

break

}

}

print("前三名:\r" + "第壹名" + "\(firstPlace)" + " 分\r" + "第二名:" + "\(secondPlace)" + " 分\r" + "第三名:" + "\(thirdPlace)" + " 分")

輸出結果為:

前三名:

第壹名:300分

第二名:230.45分

第三名:150分

從JSON數據中解碼空值

由於JSON的空值是沒有類型的,系統會將空值替換為壹個JSONConvertibleNull對象。比如:

let jsonString = "{\"第壹名\":300,\"第四名\":null,\"第二名\":230.45,\"第三名\":150}"

if let decoded = try jsonString.jsonDecode() as? [String:Any] {

for (key, value) in decoded {

if let value as? JSONConvertibleNull {

print("字段\"\(key)\"為空值")

}

}

}

輸出為:

字段"第四名"為空值

可轉換為JSON的對象

Perfect的JSON轉換工具庫提供為定制類的編碼解碼功能。只要從JSONConvertibleObject基類繼承即可,如下示例:

/// 從基類繼承為壹個可以轉化為JSON格式的定制對象。

public class JSONConvertibleObject: JSONConvertible {

/// 默認構造函數

public init() {}

/// 獲得JSON鍵/值

public func setJSONValues(_ values:[String:Any]) {}

/// 根據JSON鍵/值設置對象屬性。

public func getJSONValues() -> [String:Any] { return [String:Any]() }

/// 將對象編碼為JSON文本

public func jsonEncodedString() throws -> String {

return try self.getJSONValues().jsonEncodedString()

}

}

任何需要使用JSON編解碼的對象都首先要將該對象註冊到系統中去。註冊工作需要在您的應用程序啟動時完成。調用JSONDecoding.registerJSONDecodable函數完成對象註冊。該函數定義如下:

public class JSONDecoding {

/// 該函數為基於JSON成員數據定制對象返回壹個新的實例。

public typealias JSONConvertibleObjectCreator = () -> JSONConvertibleObject

static public func registerJSONDecodable(name: String, creator: JSONConvertibleObjectCreator)

}

註冊對象是需要壹個唯壹的命名。同樣還需要壹個creator函數用於在需要時創建壹個新的對象實例。

當系統對壹個JSONConvertibleObject對象編碼時,會調用對象的getJSONValues函數。該函數會返回壹個[String:Any]字典,該字典包含了用於給這個對象編碼的所有的字段和屬性值。這個字典必須要包含壹個聲明其對象類型的字段。而這個類型字段的值也 必須 是與該對象在程序開始階段註冊的名稱壹致的名字。對應該屬性值的字段由JSONDecoding.objectIdentifierKey屬性而定。

當系統解碼這樣壹個對象時,系統會首先尋找JSONDecoding.objectIdentifierKey值,然後在查找之前註冊的對象creator構造函數。隨後系統會根據這個類型和構造函數自動創建壹個新對象並調用setJSONValues(_ values:[String:Any]) 函數設置各字段值。調用該函數會用壹個包含所有解碼數據的字典作為參數傳遞過去。這些屬性值會與之前由getJSONValues編碼函數返回的內容進行匹配。在setJSONValues函數中,對象會恢復所有屬性與數據。

下面的例子演示了如何定義壹個定制的JSONConvertibleObject對象,以及如何將其轉換為壹個JSON字符串。然後再進行解碼並與原對象進行比較。?註意?在本例子中對象通過調用getJSONValue函數直接把壹個命名字段的屬性值從字典中抽取出來,而且允許在字典內不包含指定字段的情況下返回壹個默認值。

該例子分成了以下幾個部分逐壹說明。

類定義

class User: JSONConvertibleObject {

static let registerName = "user"

var firstName = ""

var lastName = ""

var age = 0

override func setJSONValues(_ values: [String : Any]) {

self.firstName = getJSONValue(named: "firstName", from: values, defaultValue: "")

self.lastName = getJSONValue(named: "lastName", from: values, defaultValue: "")

self.age = getJSONValue(named: "age", from: values, defaultValue: 0)

}

override func getJSONValues() -> [String : Any] {

return [

JSONDecoding.objectIdentifierKey:User.registerName,

"firstName":firstName,

"lastName":lastName,

"age":age

]

}

}

註冊定義好的類信息

// 運行壹次即可

JSONDecoding.registerJSONDecodable(name: User.registerName, creator: { return User() })

對象編碼:

let user = User()

user.firstName = "Donnie"

user.lastName = "Darko"

user.age = 17

let encoded = try user.jsonEncodedString()

編碼後的數據看起來像這樣:

{"lastName":"Darko","age":17,"_jsonobjid":"user","firstName":"Donnie"}

對象解碼:

guard let user2 = try encoded.jsonDecode() as? User else {

return // 出錯

}

// 驗證屬性值是否壹致

XCTAssert(user.firstName == user2.firstName)

XCTAssert(user.lastName == user2.lastName)

XCTAssert(user.age == user2.age)

JSON轉換錯誤

在JSON編碼解碼過程中,系統可能會拋出壹個JSONConversionError轉換異常,定義如下:

/// 在JSON編解碼過程中可能發生的錯誤異常。

public enum JSONConversionError: ErrorProtocol {

/// 對象不支持JSON轉換。

case notConvertible(Any)

/// 提供的字段不是字符串。

case invalidKey(Any)

/// JSON文本內由語法錯誤。

case syntaxError

}