iOS-SwiftyJSON个人浅析

一个最简单的使用SwiftyJSON的示例:

let a = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")

SwiftyJSON提供了多种初始化接口,根据不同类型的数据在内部进行不同的转换最终都归纳到统一的接口完成初始化:

fileprivate init(jsonObject: Any) {
        self.object = jsonObject
    }

但在此之前,多种多样或符合JSON标准或不符合的数据都要经过中转站:

public init(_ object: Any) {
        switch object {
        case let object as [JSON] where object.count > 0:
            self.init(array: object)
        case let object as [String: JSON] where object.count > 0:
            self.init(dictionary: object)
        case let object as Data:
            self.init(data: object)
        default:
            self.init(jsonObject: object)
        }
    }

通过这个函数,SwiftyJSON将数据转换为需要的Any对象,例如,当我们将一个JSON类型的数组对一个JSON进行初始化(虽然我不知道在何种状况下能达成条件):

        let a = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
        let b = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
        let c = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
        let d = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")
        let e = JSON.init(parseJSON: "{\"name\":\"dante\",\"age\":\"18\"}")

        let arr = [a,b,c,d,e]
        
        let output = JSON.init(arr)

SwiftyJSON会通过遍历JSON数组以获取代表真实数据的JSON实例对象的object属性,object属性根据数据真实类型获取对应类型的数据,最终返还给output,生成新JSON对象,这一步的作用是避免多层嵌套JSON的产生导致的结构趋向复杂。
[String: JSON]也是同理。
最后,我们获得且仅获得一个可能包含任意数据类型但表现为数据安全的object对象。

set {
            _error = nil
            switch newValue {
            case let number as NSNumber:
                if number.isBool {
                    _type = .bool
                    self.rawBool = number.boolValue
                } else {
                    _type = .number
                    self.rawNumber = number
                }
            case let string as String:
                _type = .string
                self.rawString = string
            case _ as NSNull:
                _type = .null
            case _ as [JSON]:
                _type = .array
            case nil:
                _type = .null
            case let array as [Any]:
                _type = .array
                self.rawArray = array
            case let dictionary as [String : Any]:
                _type = .dictionary
                self.rawDictionary = dictionary
            default:
                _type = .unknown
                _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
            }
        }

object属性被赋值时,逐步判断object的类型,并赋值枚举,同时向相应的代表真实数据的属性填值。
于此,SwiftyJSON完成了对数据的安全存储,保证即使数据错误也不会导致崩溃的后果,这很重要,因为每次通过json[0]["user"]["name"]诸如这样的方式去获取被SwiftyJSON存储的数据时都是在重复上述的初始化方式。

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 if index >= 0 && index < self.rawArray.count {
                return JSON(self.rawArray[index])
            } else {
                var r = JSON.null
                r._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"])
                return r
            }
        }
        set {
            if self.type == .array {
                if self.rawArray.count > index && newValue.error == nil {
                    self.rawArray[index] = newValue.object
                }
            }
        }
    }

SwiftyJSON实现了复数的下标函数分别对数字下标、字符串下标、数字及字符串多参数下标以及数字与字符串数组下标等形式的数据获取与请求方式。
如上面的源码,当调用数字下标时,首先判断调用者在初始化时赋予的类型是否是数组,若是则取出对应的真实数据数组中的真实数据,并使用新的JSON容器包裹返回,通过JSON初始化函数避免崩溃性错误以及通过这种类链式语法方便操作。
字符串下标亦是同理。

有趣的地方在于数字下标与字符串下标混合使用,就像这样:

let name = json[1,"list",2,"name"].string

跟这样:

let name = json[[1,"list",2,"name"]].string

SwiftyJSON声明了一个JSONSubscriptType协议,协议仅包含一个名为JSONKey的属性,该属性为一个枚举:

public enum JSONKey
{
    case index(Int)
    case key(String)
}

然后,通过扩展分别实现了Int类型与String类型的扩展,使其遵循协议:

extension Int: JSONSubscriptType {
    public var jsonKey:JSONKey {
        return JSONKey.index(self)
    }
}

extension String: JSONSubscriptType {
    public var jsonKey:JSONKey {
        return JSONKey.key(self)
    }
}

let name = json[1,"list",2,"name"]被调用时,其被作为一组参数传入下标函数,接着,首先忽略其过程,SwiftyJSON需要的结果是根据下标数组的顺序拆解最开始的JSON容器,每一个下标做出新的JSON容器,然后从这个JSON中取出真实数据,再用新的JSON容器包裹,直至遍历完整个下标数组。
在获得数组后,在暂时不以SwiftyJSON所写的代码为最优解时,我们其实有多种方式可以实现这个目的,比如将混合数组作为[Any]进行遍历,根据元素类型进行强转再调用JSON的下标函数,这样也可以达到相同的目的。
SwiftJSON则是以reduce函数来应对:

//path: [JSONSubscriptType]
path.reduce(self) { $0[sub: $1] }

通过reduce函数的性质,将JSON容器自身作为初始值不断地根据下标获取新容器。
sub下标函数中通过JSONKey再将IntString下标发给对应的下标函数。

switch sub.jsonKey {
            case .index(let index):
                return self[index: index]
            case .key(let key): return self[key: key]
            }

另外,SwiftyJSON也实现了循环字典及循环数组的功能。

for (key: String, subJson: JSON) in json {
   //Do something you want
}
for (index: String, subJson: JSON) in json {
    //Do something you want
}

通过声明Collection协议并实现迭代器相关函数,根据初始化时赋予的type属性判断容器中的真实数据是否为字典或者数组中的一种,调用其真实数据的迭代器函数。

public subscript (position: Index) -> (String, JSON)
    {
        switch position
        {
        case .array(let idx):
            return (String(idx), JSON(self.rawArray[idx]))
        case .dictionary(let idx):
            let (key, value) = self.rawDictionary[idx]
            return (key, JSON(value))
        default:
            return ("", JSON.null)
        }
    }

由此完成了SwiftyJSON的绝大多数功能,剩下的七百多行代码都是对导出真实数据的扩展,例如将NSNumber类型转化为Double类型导出,布尔值类型导出成数字类型等等等等,不再一一叙述。

你可能感兴趣的:(iOS-SwiftyJSON个人浅析)