开源项目:JSONNeverDie,纯 Swift 开发的全功能 JSON 解析、生成库,兼容 SwiftyJSON 主要 API:https://github.com/johnlui/JSONNeverDie
本篇文章中,我将跟大家一起,一步一步构造出一个好用的 JSON 解析和生成的库。
准备工作
起因
在我动手搞这个 JSON 解析库之前,我一直在用 SwiftyJSON 这个库,这个库是国人开源的最受欢迎的 Swift 项目,没有之一,也是全球最受欢迎的 Swift 库第二名,第一名是网络库 Alamofire。由于要实现 [“key”][“key1”] 这样的递归查找,我一直觉得 JSON 解析库非常复杂难搞。
过程
最近比较闲,我打算把之前用过的开源库都自己实现一下,提升一下自己。而且我在实际使用 SwiftyJSON 的过程中,遇到过非合法长字符串导致奔溃的情况,我打算先从 JSON 解析库下手,于是中秋节的前一天,吃完午饭我就开搞了,到了下午六七点,解析的功能就全部搞定了,十分出乎预料。中秋节这天我又把生成的功能做了,整理下代码,收拾收拾就给开源了。
API 统计
言归正传,我们的准备工作还是要做的:统计 SwiftyJSON 的主要 API。经过简单统计,我找到了所有我在项目中使用过的 SwiftyJSON 的 API,主要分为四类:
- 通过特定路径取出特定类型的值,如:json[“key”][“key1”].stringValue
- 取出某个数组类型的子 JSON,循环拿到里面的值
- 将某个 JSON 对象格式化成字符串
- 使用 Dictionary 生成 JSON 对象
递归取值
设计基本结构
既然要兼容 SwiftyJSON 的主要 API,那调用方式跟它一样就行了:先使用 NSData、Array 或者 Dictionary 生成 JSON 对象,再对这个对象进行操作,拿到我们想要的值、数组、完整的 JSON 字符串等。
为了对比 API 的执行结果,我们仍然引入 SwiftyJSON 库,所以我们需要一个其他的类名,在这里我们就暂定为 JSONND,是 JSON Never Die 的缩写,含义是永不奔溃的 JSON 解析库。
我们先从网络数据下手。网络数据的来源一般为 NSData,经过简单查询我们知道系统提供了一个 JSON 解析方法,可以把 NSData 格式的解析为 AnyObject,构造出 JSONND 类:
1 2 3 4 5 6 7 8 9 10 11 12 |
public struct JSONND { public var jsonObject: AnyObject! public static func initWithData(data: NSData) -> JSONND! { do { return JSONND(jsonObject: try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)) } catch let error as NSError { let e = NSError(domain: "JSONNeverDie.JSONParseError", code: error.code, userInfo: error.userInfo) NSLog(e.localizedDescription) return JSONND() } } } |
需要注意的是,我们给 JSON 类使用的是 struct 结构体,为了它能够具备自动初始化函数,值类型等优良特性。JSON 直观上感觉是 String 的衍生,故使用值类型也起到降低学习成本的作用。
我们使用下面的代码来检验成果:
1 2 |
let data = NSData(contentsOfURL: NSURL(string: "http://httpbin.org/get?hello=world")!)! let json = JSONND.initWithData(data) |
运行,正常,初始化代码完成。
支持 [“key”][“key1”] 形式的递归取值
为了支持递归取值,同时不让我们的 JSONND 结构体变的过于臃肿,我们考虑将递归取值的任务交给第二个结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public struct JSONNDElement { public var data: AnyObject! public init(data: AnyObject!) { self.data = data } public subscript (index: String) -> JSONNDElement { if let jsonDictionary = self.data as? Dictionary<String, AnyObject> { if let value = jsonDictionary[index] { return JSONNDElement(data: value) } else { NSLog("JSONNeverDie: No such key '\(index)'") } } return JSONNDElement(data: nil) } } |
同时,我们需要在 JSONND 结构体中触发递归取值的第一次:
1 2 3 4 |
public subscript (index: String) -> JSONNDElement { let jsonNDElement = JSONNDElement(data: self.jsonObject) return jsonNDElement[index] } |
检验成果:
1 2 3 4 |
let data = NSData(contentsOfURL: NSURL(string: "http://httpbin.org/get?hello=world")!/span>NSData(contentsOfURL: NSURL(string: "http://httpbin.org/get?hello=world")! h3>
在我动手搞这个 JSON 解析库之前,我一直在用 SwiftyJSON 这个库,这个库是国人开源的最受欢迎的 Swift 项目,没有之一,也是全球最受欢迎的 Swift 库第二名,第一名是网络库 Alamofire。由于要实现 [“key”][“key1”] 这样的递归查找,我一直觉得 JSON 解析库非常复杂难搞。 过程最近比较闲,我打算把之前用过的开源库都自己实现一下,提升一下自己。而且我在实际使用 SwiftyJSON 的过程中,遇到过非合法长字符串导致奔溃的情况,我打算先从 JSON 解析库下手,于是中秋节的前一天,吃完午饭我就开搞了,到了下午六七点,解析的功能就全部搞定了,十分出乎预料。中秋节这天我又把生成的功能做了,整理下代码,收拾收拾就给开源了。 API 统计言归正传,我们的准备工作还是要做的:统计 SwiftyJSON 的主要 API。经过简单统计,我找到了所有我在项目中使用过的 SwiftyJSON 的 API,主要分为四类:
递归取值设计基本结构既然要兼容 SwiftyJSON 的主要 API,那调用方式跟它一样就行了:先使用 NSData、Array 或者 Dictionary 生成 JSON 对象,再对这个对象进行操作,拿到我们想要的值、数组、完整的 JSON 字符串等。 为了对比 API 的执行结果,我们仍然引入 SwiftyJSON 库,所以我们需要一个其他的类名,在这里我们就暂定为 JSONND,是 JSON Never Die 的缩写,含义是永不奔溃的 JSON 解析库。 我们先从网络数据下手。网络数据的来源一般为 NSData,经过简单查询我们知道系统提供了一个 JSON 解析方法,可以把 NSData 格式的解析为 AnyObject,构造出 JSONND 类:
需要注意的是,我们给 JSON 类使用的是 struct 结构体,为了它能够具备自动初始化函数,值类型等优良特性。JSON 直观上感觉是 String 的衍生,故使用值类型也起到降低学习成本的作用。 我们使用下面的代码来检验成果:
运行,正常,初始化代码完成。 支持 [“key”][“key1”] 形式的递归取值为了支持递归取值,同时不让我们的 JSONND 结构体变的过于臃肿,我们考虑将递归取值的任务交给第二个结构体:
同时,我们需要在 JSONND 结构体中触发递归取值的第一次:
检验成果:
|