Swift json字典转模型 项目记录

背景

最近项目开始转用Swift3开发,因为Swift中json(字典)转模型的选择方案较多,笔者最开始选择了HandyJSON的方案,在使用一段时间后发现当要进行某个字段取值使用时须要进行各类的转化判断,比较麻烦(可是安全、保证程序不会抛出异常)。因而笔者引入了SwiftyJSON库。因而取值变得简单方便。git

新问题

因为SwiftyJSON的引入,笔者将网络请求基本请求完成后进行了JSON化处理,若是后面再进行HandyJSON转模型处理,就要进行二次操做,感受效率上会有影响。
固然也能够选择网络请求基类对返回数据不作任何处理,交还由各个请求发起者处理,但这样会致使大量的重复代码。在这种状况下笔者思考利用JSON本身进行模型转化。github

方案

runtime机制给模型赋值

在OC中咱们的json转模型一般是利用runtime机制获取模型对象的属性列表,再进行判断并取值赋值.因为Swift中已经逐渐放弃runtime机制。所以笔者放弃了此种方案json

反射机制给模型赋值

在Swift中有一个Mirror类,具有获取对象信息能力、能获取对象名字及值。接着再经过kvc的方式将值写入模型便可。(HandyJSON的值写入是直接经过内存地址写入)安全

实现

这里笔者经过反射机制来实现json转模型(因为项目中使用到了SwiftyJSON,所以添加了JOSN转化为模型方法)。模型基类代码以下网络

import UIKit
import SwiftyJSON

class BQModel: NSObject {
    required override init() {
        
    }
    required init(_ dic: Dictionary<String,Any>) {
        super.init()
        self.configValue(dic)
    }
    func configValue(_ dic: Dictionary<String,Any>) {
        let mirror = Mirror(reflecting: self)
        for p in mirror.children {
            let name = p.label!
            if let value = dic[name] {
                if p.value is BQModel && value is Dictionary<String, Any>   {
                    (p.value as! BQModel).configValue(value as! Dictionary<String, Any>)
                }else {
                    self.setValue(value, forKey: name)
                }
            }
        }
    }
    override var description: String {
        let mirror = Mirror(reflecting: self)
        var result = [String:Any]()
        for p in mirror.children {
            let name = p.label!
            result[name] = p.value
        }
        return String(describing: "<\(self.classForCoder):\(Unmanaged.passRetained(self).toOpaque())>\n\(result)")
    }
//    MARK:- ***** if has SWIFTJSON can use this Mesthod *****
        required init(_ json: JSON) {
            super.init()
            self.configValue(json)
        }
        func configValue(_ json: JSON) {
            if let modelInfo = json.dictionary {
                let mirror = Mirror(reflecting: self)
                for p in mirror.children {
                    let name = p.label!
                    if let value = modelInfo[name] {
                        switch value.type {
                        case .string:
                            self.setValue(value.string, forKey: name)
                        case .number:
                            self.setValue(value.number, forKey: name)
                        case .bool:
                            self.setValue(value.bool, forKey: name)
                        case .dictionary:
                            let val = self.value(forKey: name)
                            if val is BQModel {
                                (val as! BQModel).configValue(value)
                            }else {
                                self.setValue(value.dictionary, forKey: name)
                            }
                        case .array:
                            self.setValue(value.array, forKey: name)
                        default:
                            break
                        }
                    }
                }
            }
        }
}

extension Array where Element: BQModel {
    static func modelArr(arr: Array<Dictionary<String,Any>>) -> Array<Element> {
        var result = [Element]()
        for dic in arr {
            result.append(Element(dic))
        }
        return result
    }
    //MARK:- ***** if has SWIFTJSON can use this Mesthod *****
    static func modelArr(arr: Array<JSON>) -> Array<Element> {
        var result = [Element]()
        for json in arr {
            result.append(Element(json))
        }
        return result
    }
}

测试

当模型写好后,笔者进行了一个简单的测试。将一万个对象的json字符串做为网络请求返回值,模拟网络请求。
而后分别用HandyJSON和BQModel来进行模型转化app

模型代码,保证属性相同ide

class Person: BQModel {
    var name: String?
    var age: Int = 0
    var std = Student()
}
class Student: BQModel {
    var id: String?
    var num: Int = 0
    var isOld: Bool = false
}

struct ABC: HandyJSON {
    var name: String?
    var age: Int = 0
    var std = ABD()
}
struct ABD: HandyJSON {
    var id: String?
    var num: Int = 0
    var isOld: Bool = false
}

测试代码工具

//模拟网络请求返回数据JSON化处理
let json = JSON(["name":"asd","age":11,"std":["id":"123","num":12]])
//数据处理
let arrJSON = [JSON](repeating: json, count: 10000)
let arrDict = [Dictionary](repeating: ["name":"asd","age":11,"std":["id":"123","num":12]], count: 10000)
let string = BQTool.jsonFromObject(obj: arrDict)
        
//时间测试
print("BQModel with JSON")
self.getTime {
    let _ = [Person].modelArr(arr: arrJSON)
}
print("BQModel with Dict")
self.getTime {
    et data = string.data(using: .utf8)!
    let obj = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as! Array<Dictionary<String,Any>>
    let _ = [Person].modelArr(arr: obj)
}
print("HandyJSON")
self.getTime {
    let _ = [ABC].deserialize(from: string)
}

测试结果及分析

进行三次测试结果数据以下测试

test-1test-4test-3

有测试数据得出BQModel的效率在这里比HandyJSON较好。须要注意的是笔者缩写的模型考虑状况确定没有HandyJSON多。因此在通用性方面应该没有HandyJSON作的好(这里也多是HandyJSON效率比BQModel低的缘由)。另外HandyJSON采用内存地址写入,而笔者采用KVC写入,并不彻底符合Swift的特性。因此在采用哪一种模型方案的时候仍是看你们的需求和选择。最后附上笔者Swift工具库链接,有兴趣的朋友能够去看看。若是上述有什么错误请指正,谢谢!ui

相关文章
相关标签/搜索