Skip to content

Swift JSON

John Corner edited this page Sep 6, 2018 · 1 revision

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对象包含多个键值、数字、嵌套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数据

这里我们拿到的二进制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以后的版本将开始稳定,到时候才能显示它真正的实力。

Clone this wiki locally