SwiftyJSON 为json解析提供了优雅的解决方案,而且源代码并不多,其理念非常值得学习。
SwiftJSON
的核心数据结构是JSON
。JSON
就像一个工厂,我们的数据就是原材料,当把原材料交给这个工厂之后,就可以向其索要任何我们想要的数据格式,工厂会为我们处理转化过程。
public enum Type: Int { case number case string case bool case array case dictionary case null case unknown } public Struct JSON { /// 具体类型的存储成员 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 /// 类型信息 public fileprivate(set) var type: Type = .null /// 发生错误后的存储成员 public fileprivate(set) var error: SwiftyJSONError? /// 数据的存储成员,这个家伙很重要 public var object: Any { .... } } 复制代码
JSON
保存了数据的类型和原数据这两个重要的信息,接下来的解析过程都是以这两个成员为基础。
SwiftyJSON
对外提供了三个初始化器,这三个初始化器都试图先将数据转换为Data,最终会来到fileprivate init(jsonObject: Any)
方法中。
fileprivate init(jsonObject: Any) { // 触发object 成员setter object = jsonObject } public var object: Any { get { ... } //解析数据的类型,填充具体类型的成员 set { error = nil switch unwrap(newValue) { case let number as NSNumber: if number.isBool { type = .bool rawBool = number.boolValue } else { type = .number rawNumber = number } case let string as String: type = .string rawString = string case _ as NSNull: type = .null case nil: type = .null case let array as [Any]: type = .array rawArray = array case let dictionary as [String: Any]: type = .dictionary rawDictionary = dictionary default: type = .unknown error = SwiftyJSONError.unsupportedType } } } //递归的解析数据的类型 private func unwrap(_ object: Any) -> Any { switch object { case let json as JSON: return unwrap(json.object) case let array as [Any]: return array.map(unwrap) case let dictionary as [String: Any]: var d = dictionary dictionary.forEach { pair in d[pair.key] = unwrap(pair.value) } return d default: return object } } 复制代码
小结: 初始化过程首先保存的数据的副本,并且解析根对象的类型并保存,并且填充具体类型的成员
SwiftyJOSN 获取值的调用方式支持subscript
,与直接操作Dictionary
体验一致。对外提供的公有接口是public subscript(path: JSONSubscriptType...) -> JSON
,数据的获取和设置调用流程是一样的,调用流程如图:
public enum JSONKey { case index(Int) case key(String) } public protocol JSONSubscriptType { var jsonKey: JSONKey { get } } extension Int: JSONSubscriptType { public var jsonKey: JSONKey { return JSONKey.index(self) } } extension String: JSONSubscriptType { public var jsonKey: JSONKey { return JSONKey.key(self) } } extension JSON { /// 解析到当前操作的类型是array类型 fileprivate subscript(index index: Int) -> JSON { //从array类型中取值 get { if type != .array { //处理类型错误 var r = JSON.null r.error = self.error ?? SwiftyJSONError.wrongType return r } else if rawArray.indices.contains(index) { //对外只返回JSON类型 return JSON(rawArray[index]) } else { //处理数组索引错误 var r = JSON.null r.error = SwiftyJSONError.indexOutOfBounds return r } } //向array类型中设置值 set { if type == .array && rawArray.indices.contains(index) && newValue.error == nil { rawArray[index] = newValue.object } } } /// 解析到当前操作的类型是dictionary类型 fileprivate subscript(key key: String) -> JSON { //从dictionary中取值 get { var r = JSON.null if type == .dictionary { if let o = rawDictionary[key] { //包装对象对外只返回JSON r = JSON(o) } else { //不存在 r.error = SwiftyJSONError.notExist } } else { //类型错误 r.error = self.error ?? SwiftyJSONError.wrongType } return r } set { if type == .dictionary && newValue.error == nil { rawDictionary[key] = newValue.object } } } /// 对key的类型进行解析,进而决定是从array中获取还是从json中获取 fileprivate subscript(sub sub: JSONSubscriptType) -> JSON { get { switch sub.jsonKey { case .index(let index): return self[index: index] case .key(let key): return self[key: key] } } set { switch sub.jsonKey { case .index(let index): self[index: index] = newValue case .key(let key): self[key: key] = newValue } } } public subscript(path: [JSONSubscriptType]) -> JSON { get { //解析path 的key,一层一层获取 return path.reduce(self) { $0[sub: $1] } } set { switch path.count { case 0: return case 1: self[sub:path[0]].object = newValue.object default: var aPath = path aPath.remove(at: 0) //递归地设置值,先去就旧值修改,然后再设置回去,直到path的最后一个key var nextJSON = self[sub: path[0]] nextJSON[aPath] = newValue self[sub: path[0]] = nextJSON } } } //对外暴露的接口 public subscript(path: JSONSubscriptType...) -> JSON { get { return self[path] } set { self[path] = newValue } } } 复制代码
public subscript(path: [JSONSubscriptType]) -> JSON
方法完成了对paths
的拆解工作,实现的比较优雅。
使用JSONSubscriptType protocol
和 JSONKey enum
统一了key的类型,使Int
和 String
都可以作为key。
在fileprivate subscript(sub sub: JSONSubscriptType) -> JSON
解析枚举类型,枚举在这里的使用形式也值得借鉴。
通过 key
获取的类型最终还是被包装在了JSON
结构体中,要真正取到值还得要调用下面的方法:
方法较多,但都是对相应的类型都提供可选值和非可选值的版本,使用起来非常方便。 来看看最常用的 Number类型 和 String类型的实现原理
//Optional number public var number: NSNumber? { get { switch type { case .number: return rawNumber case .bool: return NSNumber(value: rawBool ? 1 : 0) default: return nil } } set { object = newValue ?? NSNull() } } //Non-optional number public var numberValue: NSNumber { get { switch type { case .string: let decimal = NSDecimalNumber(string: object as? String) return decimal == .notANumber ? .zero : decimal case .number: return object as? NSNumber ?? NSNumber(value: 0) case .bool: return NSNumber(value: rawBool ? 1 : 0) default: return NSNumber(value: 0.0) } } set { object = newValue } } 复制代码
extension JSON { //Optional string public var string: String? { get { switch type { case .string: return object as? String default: return nil } } set { object = newValue ?? NSNull() } } //Non-optional string public var stringValue: String { get { switch type { case .string: return object as? String ?? "" case .number: return rawNumber.stringValue case .bool: return (object as? Bool).map { String($0) } ?? "" default: return "" } } set { object = newValue } } } 复制代码
这两个方法的实现大同小异,都是先类型解析,然后包装值类型。 而值的类型都是在初始化时解析完成的。
SwiftyJSON 代码看下来,并没有什么难懂的概念,但是实现确很优雅,可见作者的功力。
SwiftyJSON核心思想总结为八个字:“统一类型,取值包装”。 使用核心类型JSON
包装json数据,在取值过程中不断的将中间值包装到JSON
,对外界隐匿了复杂的中间值判断,使外界只需要关心最终值的类型即可。