SwiftyJSON 为json解析提供了优雅的解决方案,并且源代码并很少,其理念很是值得学习。json
SwiftJSON
的核心数据结构是JSON
。JSON
就像一个工厂,咱们的数据就是原材料,当把原材料交给这个工厂以后,就能够向其索要任何咱们想要的数据格式,工厂会为咱们处理转化过程。swift
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
,数据的获取和设置调用流程是同样的,调用流程如图:ui
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
的拆解工做,实现的比较优雅。spa
使用JSONSubscriptType protocol
和 JSONKey enum
统一了key的类型,使Int
和 String
均可以做为key。 在fileprivate subscript(sub sub: JSONSubscriptType) -> JSON
解析枚举类型,枚举在这里的使用形式也值得借鉴。code
经过 key
获取的类型最终仍是被包装在了JSON
结构体中,要真正取到值还得要调用下面的方法:cdn
方法较多,但都是对相应的类型都提供可选值和非可选值的版本,使用起来很是方便。 来看看最经常使用的 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
,对外界隐匿了复杂的中间值判断,使外界只须要关心最终值的类型便可。