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类型导出,布尔值类型导出成数字类型等等等等,不再一一叙述。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容