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
}