-
Notifications
You must be signed in to change notification settings - Fork 2
Swift JSON
无论在CS还是BS下解析JSON数据是不可避免的,但是大多语言对这个JSON数据的处理是把它看做一个字典和数组,键值最多支持数字和字符串,一些第三方库封装支持类似url对象的功能。Swift再这一块JSON的处理上达到了语言层面的支持,能避免很多坑爹的层层if嵌套处理,配合泛型能优雅而高效的实现JSON解析。
比如对一个JSON二进制数据,一般是从URLSession里面访问网络数据得到的Data,本质是UTF8编码的字符串数据。
下面就用一个例子来看看Swift惊为天人的处理方式:
{
"name": "Mike",
"age": 18,
"icon": "https://pic.com/mike.png",
"nickName": [{
"name": "goodboy"
"id": "0001"
}, {
"name": "sniky"
"id": "0002"
}, {
"name": "alpha"
"id": "0003"
}]
}
这个JSON对象包含多个键值、数字、嵌套JSON对象。age 值为数字,icon 实际上是一个URL地址,所以我们可以先定义一个struct描述这个JSON对象(首先得遵循Codable协议):
struct User: Codable {
var name: String
var age: Int
var icon: URL
}
对于nickName,我们得再定义一个struct描述:
struct NickName: Codable {
var name: String
var id: String
}
最后我们就可以得到完整的User表述:
struct User: Codable {
var name: String
var age: Int
var icon: URL
var nickName: [NickName]
}
[NickName]是个数组,再Swift下数组本身就遵循Codable协议,所以只要数组元素遵循Codable协议即可(也就是说String、Int、URL三种类型都遵循Codable协议)。
这里我们拿到的二进制JSON数据是 rawData:
let str = "{ \"name\": \"Mike\", \"age\": 18, \"icon\": \"https://pic.com/mike.png\",\"nickName\": [{\"name\": \"goodboy\", \"id\": \"0001\"}, { \"name\": \"sniky\", \"id\": \"0002\"}, {\"name\": \"alpha\", \"id\": \"0003\"}]}"
let rawData = str.data(using: .utf8)!
然后使用JSONDecoder类解析:
let decoder = JSONDecoder()
do {
let json = try decoder.decode(User.self, from: rawData)
print(json)
} catch {
print("JSON Decoder Error:\(error.localizedDescription)")
}
没错, 只需要一句 let json = try decoder.decode(User.self, from: rawData) 即可得到一个Swift的struct类型, 不需要再层层解析键值是否存在和类型的判断。
一般来说服务器返回的数据不只是User,还有其他类型,但顶层的JSON结构是这样的:
{
"code": 200,
"msg": "",
"data": ...
}
data可能是数组也可能是字典,不同接口返回的数据基本不一样,这时候就可以使用泛型来解决这个问题:
struct ServerResponse<T: Codable>: Codable {
var code: Int
var msg: String
var data: T
}
泛型 T 被限制于遵循Codable协议. 所有可以用来解析JSON对象。
func readData<T: Codable>(fromRequest request: Request,
callback: @escaping (T?, URLResponse?, Error?) -> T?) {
session.dataTask(with: request.urlRequest, completionHandler: { (data, response, err) in
if let e = err {
print("****** Network Error is \(e.localizedDescription) ******")
let _ = callback(nil, response, e)
return
}
guard let data = data else {
print("****** Data is nil ******")
let _ = callback(nil, response, nil)
return
}
do {
let json = try JSONDecoder().decode(T.self, from: data)
print("Parser Success!")
let _ = callback(json, response, nil)
} catch {
print(error)
let _ = callback(nil, response, error)
}
}).resume()
}
上面这段代码是访问服务器服务的处理函数代码,解析核心代码变成:
let json = try JSONDecoder().decode(T.self, from: data)
实际调用的时候,是这样使用的:
readData(fromRequest: request) { (apiResponse, response, err) -> User in
if let _ = err {
return nil
}
guard let data = apiResponse else {
return nil
}
// deal with data
// ..............
return data
}
得到的data就是User类型,直接就可以用。(Swift的泛型还有一些问题,返回解析数据是为了防止编译器错误)
Swift本身定位很准确,就是给 application developer 用的,有苹果的加持,5.0以后的版本将开始稳定,到时候才能显示它真正的实力。