SwiftyJSON 源码解析 —— 一款 Swift 的 JSON 解析框架

发布于: 2016-10-30 16:09
阅读: 2167
评论: 0
喜欢: 13

「SwiftyJSON」是一款由 Swift 语言开发的 JSON 解析开源框架。

导语

SwiftyJSON 大概是我接触的第一个 Swift 框架。当时的 Swift 是 1.1 版本,Xcode 还是“能用”的 Xcode。看看现在的 Xcode,抹眼泪。

它将JSON字符串中不同类型数据进行了统一的封装,暴露简单接口,利用非常人性化“路径”就可以在JSON对象中取到想要的内容。下面就来简单解析一下源码。

构造

在SwiftyJSON中,JSON的值类型只有这几种情况:

public enum Type :Int{
    case number
    case string
    case bool
    case array
    case dictionary
    case null
    case unknown
}

SwiftyJSON在构造时需要对这几种不同的值进行封装,统一接口。下面就来看看他是如何封装的。

其主要有四个构造方法(还有一些LiteralConvertible的构造方法,在协议部分提到):

    public init(data:Data, options opt: JSONSerialization.ReadingOptions = .allowFragments, error: NSErrorPointer = nil)
    public init(_ object: Any)
    public init(_ jsonArray:[JSON])
    public init(_ jsonDictionary:[String: JSON])

除此之外还有一个从字符串构造JSON的parse方法:

    public static func parse(_ string:String) -> JSON

和一个构造空JSON的static属性:

    @available(*, unavailable, renamed:"null")
    public static var nullJSON: JSON { get { return null } }
    public static var null: JSON { get { return JSON(NSNull()) } }

上面一共六个构造方法在经过不同的处理以后,最终都指向了同一个地方:

    public var object: Any { get{ ... } set { ... } }

在这个object的set方法中,对传入的对象进行了类型筛选和保存。SwiftyJSON中真正保存数据的是:

    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

object的set方法根据不同的类型将数据分别填入了这几个属性中进行保存,同时设置了以下两个属性,一个用来保存当前JSON的数据类型,一个用来保存set过程以及存取过程中发生的错误:

    fileprivate var _type: Type = .null
    fileprivate var _error: NSError? = nil

SwiftyJSON经过这个步骤,将不同属性的数据类型封装了起来。保存数据的属性全都是fileprivate的,对于用户来说,这些我们都不必去关心。

下标

SwiftyJSON中很炫酷一点的就是可以这样写路径,根据路径一层一层的找到想要的值。最重要的是,就算值不存在,也不用担心程序crash:

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

在Swift中实现下标十分容易,只需要实现subscript方法就可以了。在JSON中,索引有两种数据类型,IntString,因此下标需要支持这两种类型。那么在SwiftyJSON中这是怎么实现的呢?

  • 使用枚举(enum)定义数据类型 在Swift中,枚举是个很强大的数据结构。索引的类型有两种情况,因此其类型为:
public enum JSONKey {
    case index(Int)
    case key(String)
}
  • 定义JSONSubscriptType协议
public protocol JSONSubscriptType {
    var jsonKey:JSONKey { get }
}
  • 使用扩展让IntString遵守协议
extension Int: JSONSubscriptType {
    public var jsonKey:JSONKey {
        return JSONKey.index(self)
    }
}
extension String: JSONSubscriptType {
    public var jsonKey:JSONKey {
        return JSONKey.key(self)
    }
}

定义JSONSubscriptType协议的优点在于在写法上能统一IntString。语法糖,它们都遵守了这个协议,因此就可以这样接收参数:

fileprivate subscript(sub sub: JSONSubscriptType) -> JSON
public subscript(path: [JSONSubscriptType]) -> JSON
public subscript(path: JSONSubscriptType...) -> JSON

这三个方法将接收六种形态的参数。最终这三个方法将索引分发到下面两个方法:

fileprivate subscript(index index: Int) -> JSON
fileprivate subscript(key key: String) -> JSON

那么如果在索引过程中,索引是非法的会发生什么呢?例如在一个非Array的JSON中使用Int会发生什么呢?我们直接看源码:

    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 { ... }
        }
        set { ... }
    }

如果当前JSON不是Array,将会返回一个空的JSON,而不是直接返回nil。如果接下来还有路径,将会继续返回空JSON,从而避免了非法索引可能造成的问题。

下标赋值时也是同理,由于subscriptget & set。下标赋值时也会按照上述的路径进行分发,最终到到set中进行赋值,如果路径是非法的,那么什么都不会发生。

其他协议

  • Collection协议 Collection协议包含了IndexableSequence协议。由于数据类型中ArrayDictionary遵守了Collection协议,SwiftyJSON遵守Collection协议只为ArrayDictionary这两种数据类型服务。其余情况都将返回空JSON。

  • Comparable协议 规定了JSON对象之间的比较方法,如果两端JSON真正数据类型不同将返回false。个人看来比较的意义不是很大,因为在不确定两个JSON对象真正数据类型的情况下进行比较,无法判断到底是比较结果是false还是因为类型不匹配而返回false

  • LiteralConvertible相关协议 SwiftyJSON遵守了一些LiteralConvertible相关的协议。这些协议扩展了一些构造方法,代表JSON对象也可以由StringInt系列、Boolean系列、Float系列等等这些数据类型构造。RawRepresentable协议中也扩展了利用raw数据进行构造的方法,这些构造方法的构造过程最终也是走了上面所说的object参数的set过程。

  • CustomStringConvertibleCustomStringConvertible协议 自定义了description以及debugDescription字符串。

结语

SwiftyJSON的实现虽然简单但是十分实用。之后Alamofire推出了「Alamofire-SwiftyJSON」和SwiftyJSON协作,业务开发中的JSON网络请求一气呵成,提升了开发效率。使用开源框架固然重要,但更重要的是能从这些框架中学到什么。


Thanks for reading.

All the best wishes for you! 💕