美文网首页swift
SwiftyJSON 源码分析

SwiftyJSON 源码分析

作者: 微笑_d797 | 来源:发表于2019-03-10 22:15 被阅读0次

    周末闲着无聊打算研究一下SwiftyJSON的源码,一边看RNG打SS一边撸源码。自己学习,有问题的话也欢迎留言,一起进步

    SwiftyJSON的源码我是在github下载的.

    SwityJSON里面定义了一个JSON结构体,还有定义了他自用的一套error,这是他基本的一个构成

    SwiftyJSON定义的error

    首先分析error,这个没什么好说的根据字面意思很好理解。


    image.png
    image.png

    结构体JSON

    INIT

    我觉得分析一个源码首先要看他怎么用,这个大家都会所以这里简单说下他的用法,当我们收到服务端的数据的时候,会使用

    let data = response.result.value
    let son = JSON.init(obj: data)
    

    我们看里面是怎么实现的


    image.png

    我们发现不论什么方式所有的初始化方式都走向了


    image.png

    那既然这样我们看一下那个object到底是个什么玩意

    /// Object in JSON
        public var object: Any {
            get {
                switch self.type {
                case .array:
                    return self.rawArray
                case .dictionary:
                    return self.rawDictionary
                case .string:
                    return self.rawString
                case .number:
                    return self.rawNumber
                case .bool:
                    return self.rawBool
                default:
                    return self.rawNull
                }
            }
            set {
                error = nil
                switch unwrap(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 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 = SwiftyJSONError.unsupportedType
                }
            }
        }
    

    可以看到这个object的get,set方法。
    通过objcet的set方法我们可以知道这里是给了object的一个数据类型
    将其转化为不同的属性从而为type赋值来判断他是一个什么类型的JSON,这里如果他没有成功转化为上面的任何数据类型,则返回一个错误


    image.png

    get方法不用多说就是根据type去到对应的数据然后获取对应的数据。
    到此初始化就已经完成了


    image.png

    SwityJSon里面还有 ExpressibleByStringLiteral、ExpressibleByIntegerLiteral、ExpressibleByBooleanLiteral、ExpressibleByFloatLiteral、ExpressibleByDictionaryLiteral、ExpressibleByArrayLiteral 这几个协议来实现的,这几个协议是很有意思的东西可以帮住我们省掉很多代码, 后续我可能会在其他博客里面介绍他

    merge

    但是我注意到在初始化的方法边上还有一个merge方法,看到第一眼我认为他是一个合并的方法,就像git命令 git merge dev,就是从dev分支合并到当前分支

    /**
         Merges another JSON into this JSON, whereas primitive values which are not present in this JSON are getting added,
         present values getting overwritten, array values getting appended and nested JSONs getting merged the same way.
     
         - parameter other: The JSON which gets merged into this JSON
        
         - throws `ErrorWrongType` if the other JSONs differs in type on the top level.
    将另一个JSON合并到这个JSON中,而这个JSON中不存在的值将被添加,
    当前值被覆盖,数组值被附加(add),嵌套JSON被合并的方式相同。
    -参数:合并到这个JSON中的JSON
    -如果其他JSON在顶层的类型不同,则抛出“errorwrontype”。
         */
        public mutating func merge(with other: JSON) throws {
            try self.merge(with: other, typecheck: true)
        }
    
        /**
         Merges another JSON into this JSON and returns a new JSON, whereas primitive values which are not present in this JSON are getting added,
         present values getting overwritten, array values getting appended and nested JSONS getting merged the same way.
        
         - parameter other: The JSON which gets merged into this JSON
        
         - throws `ErrorWrongType` if the other JSONs differs in type on the top level.
        
         - returns: New merged JSON
         */
    这个方法是合并当前json并返回一个新的json对象,原来的json数据不改变。看他实现就知道声明了一个merged = self ,而self是结构体,结构体是值引用,只能深拷贝,所以会复制出一个新的对象
        public func merged(with other: JSON) throws -> JSON {
            var merged = self
            try merged.merge(with: other, typecheck: true)
            return merged
        }
    
        /**
         Private woker function which does the actual merging
         Typecheck is set to true for the first recursion level to prevent total override of the source JSON
        */
         这个方法的实现就很厉害了,如果other是字典他这里执行的是 func merge(with other: JSON),在合并该对象的时候将字身也改变,
    数组不用说,就是进行了一个add操作,如果json的对象不是本身对象的话,则判断有没有进行类型检查如果检查了跑出错误否则强行转换,所以这个方法设计为私有的,避免乱用这个方法
        fileprivate mutating func merge(with other: JSON, typecheck: Bool) throws {
            if self.type == other.type {
                switch self.type {
                case .dictionary:
                    for (key, _) in other {
                        try self[key].merge(with: other[key], typecheck: false)
                    }
                case .array:
                    self = JSON(self.arrayValue + other.arrayValue)
                default:
                    self = other
                }
            } else {
                if typecheck {
                    throw SwiftyJSONError.wrongType
                } else {
                    self = other
                }
            }
        }
    

    JSON结构

    这是一个简单json的取值

    let data = 
    {
      "result":0,
      "data": {
            "msg":"hello"
        },
       "info": [{
              "id": 0
            },{
                id: 1
          }]
    }
    
    /// 取值
    let json = JSON.init(object: data) 
    let result = json["result"].intValue or  json["result"].int?
    let data:[String:Any] = json["data"].dictionaryobject
    let info:Array = json["info"].arrayobject of   json["info"].array?
    

    那么分析他取值操实现流程,注意到了这个方法有一个subscript关键字,这个拓展里面的功能全部是取值(代码注释)

    数组取值赋值

    注释写的很清楚 如果调用该方法的类型type是array返回array[index]的object ,否则跑出 SwiftyJSONError.indexOutOfBounds异常,赋值也是如果数组越界跑出 SwiftyJSONError.indexOutOfBounds异常

        /// If `type` is `.array`, return json whose object is `array[index]`, otherwise return null json with error.
        fileprivate subscript(index index: Int) -> JSON {
            get {
                if self.type != .array {
                    var r = JSON.null
                    r.error = self.error ?? SwiftyJSONError.wrongType
                    return r
                } else if self.rawArray.indices.contains(index) {
                    return JSON(self.rawArray[index])
                } else {
                    var r = JSON.null
                    r.error = SwiftyJSONError.indexOutOfBounds
                    return r
                }
            }
            set {
                if self.type == .array &&
                    self.rawArray.indices.contains(index) &&
                    newValue.error == nil {
                    self.rawArray[index] = newValue.object
                }
            }
        }
    

    字典取值赋值

    他的type如果是字典那么返回的数据是key对应的数据,如果没有则跑出错误,SwiftyJSONError.notExist ,如果不是字典的话则跑出SwiftyJSONError.wrongType的错误,set方法则是直接给key赋值从而改变json结构

        /// If `type` is `.dictionary`, return json whose object is `dictionary[key]` , otherwise return null json with error.
    (**wrongType**)
        fileprivate subscript(key key: String) -> JSON {
            get {
                var r = JSON.null
                if self.type == .dictionary {
                    if let o = self.rawDictionary[key] {
                        r = JSON(o)
                    } else {
                        r.error = SwiftyJSONError.notExist
                    }
                } else {
                    r.error = self.error ?? SwiftyJSONError.wrongType
                }
                return r
            }
            set {
                if self.type == .dictionary && newValue.error == nil {
                    self.rawDictionary[key] = newValue.object
                }
            }
        }
    

    int或者String

    这里有点难理解大概意思就是这个方法参数是一个协议JSONSubscriptType,实现他的协议是Int和String,意思是传入的参数只能是Int或者String类型的,如果传如参数是Int类型的那么数组取值赋值,如果穿入是String类型那么执行字典取值赋值,这个方法可能写的有点过分,其实仔细一看 self[index: index]/self[key: key],不就是调用的字典取值赋值方法参数是key么 index/key参数可以省略就好理解了,我觉得这个方法是相当巧妙的将参数传了进来。

    我的想法如果让我写可能协议就是一个value,里面然后实现这个协议的value 返回self类似于这样

    public protocol JSONSubscriptType {
          var value: Any
    }
    
    extension Int: JSONSubscriptType {
        public var value: JSONKey {
            return self
        }
    }
    
    extension String: JSONSubscriptType {
        public var value: JSONKey {
            return self
        }
    }
    

    然后使用的时候再判断数据类型。

    SwiftyJSON 的实现

    image.png

    /// 代码

        /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`,  return `subscript(key:)`.
        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
                }
            }
        }
    

    路径取值

    这两个个方法注释已经写的很明白了没必要解释了运用了一个Reduce函数来实现数据path的层层筛取

        /**
         Find a json in the complex data structures by using array of Int and/or String as path.
        
         Example:
        
         ```
         let json = JSON[data]
         let path = [9,"list","person","name"]
         let name = json[path]
         ```
        
         The same as: let name = json[9]["list"]["person"]["name"]
        
         - parameter path: The target json's path.
        
         - returns: Return a json found by the path or a null json with error
         */
        public subscript(path: [JSONSubscriptType]) -> JSON {
            get {
                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)
                    var nextJSON = self[sub: path[0]]
                    nextJSON[aPath] = newValue
                    self[sub: path[0]] = nextJSON
                }
            }
        }
    
        /**
         Find a json in the complex data structures by using array of Int and/or String as path.
    
         - parameter path: The target json's path. Example:
    
         let name = json[9,"list","person","name"]
    
         The same as: let name = json[9]["list"]["person"]["name"]
    
         - returns: Return a json found by the path or a null json with error
         */
        public subscript(path: JSONSubscriptType...) -> JSON {
            get {
                return self[path]
            }
            set {
                self[path] = newValue
            }
        }
    }
    

    RawRepresentable

    SwiftyJSON还实现了RawRepresentable协议具体使用大家可以去查阅文档我这里说说SwiftyJSON在这里的实现

    image.png
    public protocol RawRepresentable {
        associatedtype RawValue
        
        public init?(rawValue: Self.RawValue)
    
        public var rawValue: Self.RawValue { get }
    }
    

    前面几个都好说init时候写的是

        public init?(rawValue: Any) {
            if JSON(rawValue).type == .unknown {
                return nil
            } else {
                self.init(rawValue)
            }
        }
    
        public var rawValue: Any {
            return self.object
        }
    

    这个分类里面还有一个 rawString 和 rawdata 方法
    就是将其转为json的字符串

    json转array 、string 、dictionary、bool、Number 等

    json对象转数组字符串或者字典,运用了n个分类,这里我们只讲array。
    var array: [JSON] 是将rawArray map 遍历然后返回JSON对象格式的数组
    var arrayValue:[JSON] 是将array 判控其底部依然执行array 然后 加上了?? 0
    var arrayobject:[Any] 即最开始初始化的 rawArray返回,也可以进行取值赋值操作

    // MARK: - Array
    
    extension JSON {
    
        //Optional [JSON]
        public var array: [JSON]? {
            if self.type == .array {
                return self.rawArray.map { JSON($0) }
            } else {
                return nil
            }
        }
    
        //Non-optional [JSON]
        public var arrayValue: [JSON] {
            return self.array ?? []
        }
    
        //Optional [Any]
        public var arrayObject: [Any]? {
            get {
                switch self.type {
                case .array:
                    return self.rawArray
                default:
                    return nil
                }
            }
            set {
                if let array = newValue {
                    self.object = array
                } else {
                    self.object = NSNull()
                }
            }
        }
    }
    

    对URL的拓展

    SwiftyJSON里面有一个拓展是如果type是String进行url的正则判断,说实话我不知道为什么这个分类里面要加入这个判断

     //Optional URL
        public var url: URL? {
            get {
                switch self.type {
                case .string:
                    // Check for existing percent escapes first to prevent double-escaping of % character
                    if self.rawString.range(of: "%[0-9A-Fa-f]{2}", options: .regularExpression, range: nil, locale: nil) != nil {
                        return Foundation.URL(string: self.rawString)
                    } else if let encodedString_ = self.rawString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) {
                        // We have to use `Foundation.URL` otherwise it conflicts with the variable name.
                        return Foundation.URL(string: encodedString_)
                    } else {
                        return nil
                    }
                default:
                    return nil
                }
            }
            set {
                self.object = newValue?.absoluteString ?? NSNull()
            }
        }
    

    SwityJSON 对NSNumber的优化

    因为NSNumber是使用了类簇设计模式,他分为 Int, Double, Float, Int8, Int16, Int32, Int64
    所以SwiftyJSON又对这些分类一次进行了优化,我觉得这是相当细心的了判断基本和string一样里面有IntValue Int 等
    这里他是加了一层判断对不能转化为NSNumber的类型进行了NSNull处理,我觉得在数据类型安全方面做到了不错的处理.
    下面以Int为例

     public var int: Int? {
            get {
                return self.number?.intValue
            }
            set {
                if let newValue = newValue {
                    self.object = NSNumber(value: newValue)
                } else {
                    self.object = NSNull()
                }
            }
        }
    
        public var intValue: Int {
            get {
                return self.numberValue.intValue
            }
            set {
                self.object = NSNumber(value: newValue)
            }
        }
    

    Codable

    Swityjson支持Codable,Codable提供了简洁的API,使Swift的编码与解析焕然一新,后续可能会在其他博客里介绍JSONEncoder,JSONDecoder,NSKeyedArchiver,NSKeyedUnarchiver与Codable的用法

    总结

    1. SwiftyJSON 初始化最终是内部生成了一个object,并生成了全局的
      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
      /// JSON type, fileprivate setter
      public fileprivate(set) var type: Type = .null
      object为其中一个值

    2. 重载(subscript)来实现对 字典 数组 path类型的取值赋值

    3. 使用RawRepresentable协议来实现了对数据的拓展。

    遵循RawRepresentable协议的类型可以表示另一个类型,并且可以通过 rawValue 这个属性得到它表示的值

    extension Animal: RawRepresentable {
        typealias RawValue = String
        var rawValue: String
    
        init(rawValue: String) {
          self.rawValue = rawValue
        } 
    }
    class Dog: Animal {
    }
    
    let dog = Dog.init(rawValue: "狗")
    dog.rawValue = "狗"
    ///枚举一般都是实现了这个协议
    
    1. 支持Codable。

    相关文章

      网友评论

        本文标题:SwiftyJSON 源码分析

        本文链接:https://www.haomeiwen.com/subject/siyppqtx.html