如何利用反射简化ObjectMapper中多属性类的mapping函数实现
嘿,我完全懂你手动写ObjectMapper的mapping函数时的痛苦——属性一多简直是重复劳动!你的反射思路方向没错,但问题出在Swift反射的局限性和ObjectMapper<-操作符的工作逻辑上,我来帮你理清楚并给出可行的解决方案:
先说说你之前尝试的问题
你两次尝试都没成功,核心原因都是一样的:
- 第一次尝试:
self.value(forKey: name)返回的是属性的值拷贝,而ObjectMapper的<-操作符需要的是属性的可变引用(inout参数)。修改这个拷贝根本不会影响原对象的属性,所以映射完全不会生效。 - 第二次尝试:Mirror的
Child只是个元组类型别名(label: String?, value: Any),其中的value同样是属性的拷贝,既没法作为inout参数传给<-操作符,编译器也不允许直接对元组值使用这个操作符,所以写法本身就无法通过编译。
可行的自动映射方案
根据你的类是否继承自NSObject,有两种不同的实现方式:
方案1:继承自NSObject的类(借助OC运行时)
如果你的模型类继承自NSObject,可以利用Objective-C运行时直接获取并修改属性,实现完全自动的映射:
import ObjectMapper import ObjectiveC class BaseAutoMappable: NSObject, Mappable { required init?(map: Map) {} func mapping(map: Map) { var propertyCount: UInt32 = 0 guard let properties = class_copyPropertyList(type(of: self), &propertyCount) else { return } for index in 0..<Int(propertyCount) { let property = properties[index] let propertyName = String(cString: property_getName(property)) // 从map中解析值并通过KVC设置到属性上 if let parsedValue = map[propertyName].value() { setValue(parsedValue, forKey: propertyName) } } free(properties) } } // 用法示例 class User: BaseAutoMappable { var username: String? var age: Int? var weight: Double? var array: [String]? var dictionary: [String: Any]? var bestFriend: User? var friends: [User]? }
方案2:纯Swift类(借助KeyPath)
如果是不继承NSObject的纯Swift类,没法用OC运行时,我们可以通过KeyPath来实现半自动化的映射(比手动写每个<-简洁很多):
import ObjectMapper protocol AutoMappable: Mappable { static var propertyKeyPaths: [PartialKeyPath<Self>] { get } } extension AutoMappable { func mapping(map: Map) { for keyPath in Self.propertyKeyPaths { // 处理可选类型属性 if let writablePath = keyPath as? WritableKeyPath<Self, Any?> { self[keyPath: writablePath] <- map[keyPath._kvcKeyPathString!] } // 处理非可选类型属性 else if let writablePath = keyPath as? WritableKeyPath<Self, Any> { self[keyPath: writablePath] <- map[keyPath._kvcKeyPathString!] } } } } // 用法示例 class User: AutoMappable { var username: String = "" var age: Int = 0 var weight: Double = 0.0 var array: [String] = [] var dictionary: [String: Any] = [:] var bestFriend: User? var friends: [User] = [] required init?(map: Map) {} static var propertyKeyPaths: [PartialKeyPath<User>] { return [\User.username, \User.age, \User.weight, \User.array, \User.dictionary, \User.bestFriend, \User.friends] } }
注意事项
- 如果JSON键和属性名不一致,这种自动映射就不适用了,你需要单独手动处理这些特殊属性。
- 嵌套的Mappable对象(比如示例中的
bestFriend)只要也实现了Mappable协议,ObjectMapper会自动完成嵌套解析。
内容的提问来源于stack exchange,提问作者Olcay Ertaşa
相关产品推荐
相关产品推荐

