教你如何用Swift写个json转模型的开源库
在iOS项目开发过程中,我们经常会用到将从服务器获取的 json 转 model 的操作,我们可以使用 Swift 提供的 使用上面两个方法只能将字典转换成 model,如果 json 最外层是个数组,那么我们就必须在循环中使用这个方法,这非常不方便, 而且还有个条件,就是 model 中的所有属性名必须跟字典中的 key 完全对应,这样就会遇到另外一个问题,如果我们字典中的一个 key 与系统关键字重名,那我们在 model 就不能使用这个 key 作为属性名了。 为了解决上面的问题,我们会使用一些第三方库去完成字典转模型的操作,例如 MJExtension 。由于它是一个 OC 的库,所以在 Swift 项目中需要引入桥接文件。在 Swift 中使用其 API 时其实是很不 swift 的。所以现在我们就用 Swift 3.0 来写一个 swift style 的 json 转模型的库吧。 例如我们有这样的两个 model: class User: NSObject { var name: String? var age = 0 var desc: String? } class Repos: NSObject { var title: String? var owner: User? var viewers: [User]? } 最终我们想实现这样的调用: let repos = json ~> Repos.self // 将一个字典转成一个Repos的实例 let viewers = viewers => User.self //将一个数组转换成User的数组
public func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T? public func =><T: NSObject>(lhs: Any,rhs: T.Type) -> [T]? 这里给出我的实现 ModelSwift。大家可以先看看我的实现然后试着写出自己的实现。好了,现在就让我们开始吧。 要解决的问题由于将数组转成模型数组,其实要做的工作跟将字典转模型是一样的,只是做了个循环而已。所以我们首先要解决的问题是:如何在 Swift 将字典转成模型。这里我们是使用 KVC就可以了。我们使用 NSObject 的 从上面要实现的效果来看,我们在使用前并不用先实例化一个对象。所以我们要解决的第二个问题是:如何通过类型来实例化一个对象。 另一个要解决的问题是字典中的 key 与关键字重名,或者我们想使用自己的名字。所以我们要实现自己的映射的策略。 还有一个问题是,如果我们服务器返回的字典数据中包含另外一个字典数组,对应我们的 model 中就是一个对象包含另外一个对象的数组。那么我们怎样才能知道这个数组中对象的类型呢? 实现思路对于上面提到的第一问题我在上面已经给出了解决方案,就是让我们的 model 继承 NSObject,然后使用 MirrorSwift 的反射机制是基于一个叫 Mirror 的 Mirror 有个 public let children: Mirror.Children 这里的 public typealias Children = AnyCollection<Mirror.Child> 可以看到它是 Child 的集合。所以我们可以通过 Mirror 的 我们写个类来测试一下: class Person: NSObject { var name = "" var age = 0 var friends: [Person]? } let mirror = Mirror(reflecting: Person()) for case let (label?,value) in mirror.children { print ("(label) = (value)") } 运行结果是如下: name = age = 0 friends = nil Mirror 还有一个类型为 func subjectType(of subject: Any) -> Any.Type { let mirror = Mirror(reflecting: subject) return mirror.subjectType } func children(of subject: Any) { let mirror = Mirror(reflecting: subject) for case let(label?,value) in mirror.children { print ("(label) = (subjectType(of: value))") } } children(of: Person()) 打印结果是这样的: name = String age = Int friends = Optional<Array<Person>> 我原本想使用这个方法来得到 model 中包含的另外对象的类型和数组中对象的类型,例如 Person 中有 class Person: NSObject { var name = "" var age = 100 var father: Person? var friends: [Person]? } 但是发现结果是 通过类型来实例化一个对象要使用 Mirror 来获得反射对象的所有属性名,就必须先使用
还是以上面的 Person 为例,我们看看这样的调用: Person.self().age // 结果是:100 所以我们通过一个类的 public typealias AnyClass = AnyObject.Type 我们通过类的 Person.self //结果是:Person.Type 得到类的类型后,通过调用其 Person.self.init().age // 结果是:100
写个简单的 josn 转模型有了上面的基础就可以来实现我们的 josn 转模型了。首先我们来写出 infix operator ~> func ~><T: NSObject>(lhs: Any,rhs: T.Type) -> T? { guard let json = lhs as? [String: Any],!json.isEmpty else { return nil } let obj = T.self() let mirror = Mirror(reflecting: obj) for case let(label?,value) in mirror.children { print ("(label) = (value)") } return obj } class Person: NSObject { var name = "" var age = 0 override var description: String { return "name = (name),age = (age)" } } let json: [String: Any] = ["name": "jewelz","age": 100] let p = json ~> Person.self // 打印结果: // name = // age = 0 通过上面的几行代码我们确实成功的创建了一个 Person 的实例了。下一步就是给实例设置值了。我们在上面的 // 从字典中获取值 if let value = json[label] { obj.setValue(value,forKey: label) } 整个代码就是这样的: infix operator ~> func ~><T: NSObject>(lhs: Any,_) in mirror.children { // 从字典中获取值 if let value = json[label] { obj.setValue(value,forKey: label) } } return obj } let p = json ~> Person.self print(p!) //结果:name = jewelz,age = 100 有了上面 infix operator => func =><T: NSObject>(lhs: Any,rhs: T.Type) -> [T]? { guard let array = lhs as? [Any],!array.isEmpty else { return nil } return array.flatMap{ $0 ~> rhs } } 上面只是实现了一个简单 josn 转模型,其实在实际项目中要解决的问题还有很多。现在再来看看我在文章开头给出的 User 类和 Respo 类: class User: NSObject { var name: String? var age = 0 var desc: String? } class Repos: NSObject { var title: String? var owner: User? var viewers: [User]? } 只简单的用上面的实现是无法得到想要的结果的。对于 User 类来说, public protocol Reflectable: class { var reflectedObject: [String: Any.Type] { get } } 在需要做映射的类中去实现该协议。 对于更复杂的 Repos 类来说,要做的事情更多。比如 写在最后通过上面的几个步骤,我们就能很快的实现一个简单的 json 转模型的需求了。总结起来就是以下几点:
对于在最后提出的几个问题,我这里就不一一详细地说明了。大家可以点这里看看我的实现。大家可以使用 CocoaPods 或者 Carthage 将 ModelSwift 集成到项目中。如果在使用中有什么问题可以 issue 我,也可以给个 star 持续关注。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |