SwiftyJSON 源码解析 —— 一款 Swift 的 JSON 解析框架

发布于:2016-10-30 16:09,阅读数:2899,点赞数:13


> [「SwiftyJSON」](https://github.com/SwiftyJSON/SwiftyJSON)是一款由 Swift 语言开发的 JSON 解析开源框架。 # 导语 SwiftyJSON 大概是我接触的第一个 Swift 框架。当时的 Swift 是 1.1 版本,Xcode 还是“能用”的 Xcode。看看现在的 Xcode,抹眼泪。 它将JSON字符串中不同类型数据进行了统一的封装,暴露简单接口,利用非常人性化“路径”就可以在JSON对象中取到想要的内容。下面就来简单解析一下源码。 # 构造 在SwiftyJSON中,JSON的值类型只有这几种情况: ```swift public enum Type :Int{ case number case string case bool case array case dictionary case null case unknown } ``` SwiftyJSON在构造时需要对这几种不同的值进行封装,统一接口。下面就来看看他是如何封装的。 其主要有四个构造方法(还有一些`LiteralConvertible`的构造方法,在协议部分提到): ```swift public init(data:Data, options opt: JSONSerialization.ReadingOptions = .allowFragments, error: NSErrorPointer = nil) public init(_ object: Any) public init(_ jsonArray:[JSON]) public init(_ jsonDictionary:[String: JSON]) ``` 除此之外还有一个从字符串构造JSON的parse方法: ```swift public static func parse(_ string:String) -> JSON ``` 和一个构造空JSON的static属性: ```swift @available(*, unavailable, renamed:"null") public static var nullJSON: JSON { get { return null } } public static var null: JSON { get { return JSON(NSNull()) } } ``` 上面一共六个构造方法在经过不同的处理以后,最终都指向了同一个地方: ```swift public var object: Any { get{ ... } set { ... } } ``` 在这个object的set方法中,对传入的对象进行了类型筛选和保存。SwiftyJSON中真正保存数据的是: ```swift fileprivate var rawArray: [Any] = [] fileprivate var rawDictionary: [String : Any] = [:] fileprivate var rawString: String = "" fileprivate var rawNumber: NSNumber = 0 fileprivate var rawNull: NSNull = NSNull() fileprivate var rawBool: Bool = false ``` object的set方法根据不同的类型将数据分别填入了这几个属性中进行保存,同时设置了以下两个属性,一个用来保存当前JSON的数据类型,一个用来保存set过程以及存取过程中发生的错误: ```swift fileprivate var _type: Type = .null fileprivate var _error: NSError? = nil ``` SwiftyJSON经过这个步骤,将不同属性的数据类型封装了起来。保存数据的属性全都是`fileprivate`的,对于用户来说,这些我们都不必去关心。 # 下标 SwiftyJSON中很炫酷一点的就是可以这样写路径,根据路径一层一层的找到想要的值。最重要的是,就算值不存在,也不用担心程序crash: ```swift let name = json[1]["list"][2]["name"].string ``` 在Swift中实现下标十分容易,只需要实现`subscript `方法就可以了。在JSON中,索引有两种数据类型,`Int`和`String`,因此下标需要支持这两种类型。那么在SwiftyJSON中这是怎么实现的呢? - 使用枚举(`enum`)定义数据类型 在Swift中,枚举是个很强大的数据结构。索引的类型有两种情况,因此其类型为: ```swift public enum JSONKey { case index(Int) case key(String) } ``` - 定义`JSONSubscriptType`协议 ```swift public protocol JSONSubscriptType { var jsonKey:JSONKey { get } } ``` - 使用扩展让`Int`和`String`遵守协议 ```swift extension Int: JSONSubscriptType { public var jsonKey:JSONKey { return JSONKey.index(self) } } extension String: JSONSubscriptType { public var jsonKey:JSONKey { return JSONKey.key(self) } } ``` 定义`JSONSubscriptType`协议的优点在于在写法上能统一`Int`和`String`。语法糖,它们都遵守了这个协议,因此就可以这样接收参数: ```swift fileprivate subscript(sub sub: JSONSubscriptType) -> JSON public subscript(path: [JSONSubscriptType]) -> JSON public subscript(path: JSONSubscriptType...) -> JSON ``` 这三个方法将接收六种形态的参数。最终这三个方法将索引分发到下面两个方法: ```swift fileprivate subscript(index index: Int) -> JSON fileprivate subscript(key key: String) -> JSON ``` 那么如果在索引过程中,索引是非法的会发生什么呢?例如在一个非`Array`的JSON中使用`Int`会发生什么呢?我们直接看源码: ```swift fileprivate subscript(index index: Int) -> JSON { get { if self.type != .array { var r = JSON.null r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"]) return r } else { ... } } set { ... } } ``` 如果当前JSON不是`Array`,将会返回一个空的JSON,而不是直接返回nil。如果接下来还有路径,将会继续返回空JSON,从而避免了非法索引可能造成的问题。 下标赋值时也是同理,由于`subscript`是`get & set`。下标赋值时也会按照上述的路径进行分发,最终到到`set`中进行赋值,如果路径是非法的,那么什么都不会发生。 # 其他协议 - `Collection`协议 `Collection`协议包含了`Indexable`和`Sequence`协议。由于数据类型中`Array`和`Dictionary`遵守了`Collection`协议,SwiftyJSON遵守`Collection`协议只为`Array`和`Dictionary`这两种数据类型服务。其余情况都将返回空JSON。 - `Comparable`协议 规定了JSON对象之间的比较方法,如果两端JSON真正数据类型不同将返回`false`。个人看来比较的意义不是很大,因为在不确定两个JSON对象真正数据类型的情况下进行比较,无法判断到底是比较结果是`false`还是因为类型不匹配而返回`false`。 - `LiteralConvertible`相关协议 SwiftyJSON遵守了一些`LiteralConvertible `相关的协议。这些协议扩展了一些构造方法,代表JSON对象也可以由`String`、`Int`系列、`Boolean`系列、`Float`系列等等这些数据类型构造。`RawRepresentable`协议中也扩展了利用raw数据进行构造的方法,这些构造方法的构造过程最终也是走了上面所说的`object`参数的`set`过程。 - `CustomStringConvertible`和`CustomStringConvertible`协议 自定义了`description`以及`debugDescription`字符串。 # 结语 SwiftyJSON的实现虽然简单但是十分实用。之后Alamofire推出了[「Alamofire-SwiftyJSON」](https://github.com/SwiftyJSON/Alamofire-SwiftyJSON)和SwiftyJSON协作,业务开发中的JSON网络请求一气呵成,提升了开发效率。使用开源框架固然重要,但更重要的是能从这些框架中学到什么。


评论:0条


返回列表

返回主页