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
,對外界隱匿了複雜的中間值判斷,使外界只須要關心最終值的類型便可。