在日常开发活动中, 很多时候都需要将某个实体转换为字典, 而转换字典的方式有千千万万种, 这里介绍一种利用 Swift 的反射获取属性并转换的方式.
原理就是利用 Mirror
类提供的能力将实体中所有属性都遍历出来, 然后将属性名作为 key, 属性值作为 value 存入到字典中.
首先定义一个协议, 有了协议之后可以很方便地为协议提供默认实现, 从而避免对每个实体类型都单独写转换方法:
/// 字典转换协议
protocol DictionaryConvertible {
/// 将实体转换为字典.
///
/// - Returns: 实体对应的字典
func toDictionary() -> [String: Any]
}
定义好了这个协议, 就可以给这个协议提供默认实现了:
extension DictionaryConvertible {
func toDictionary() -> [String: Any] {
var dictionary = [String: Any]()
let mirrorSelf = Mirror(reflecting: self)
mirrorSelf.children.forEach({ label, value in
guard let key = label else { return }
if let array = value as? Array<DictionaryConvertible> {
dictionary[key] = array.map({ $0.toDictionary() })
} else if let convertible = value as? DictionaryConvertible {
dictionary[key] = convertible.toDictionary()
} else {
dictionary[key] = value
}
})
return dictionary
}
}
其中考虑了实体属性是如下两种类型的可能:
-
属性是
DictionaryConvertible
, 则对该属性的值也进行字典转换. -
属性是
DictionaryConvertible
数组, 则将数组的每个元素都转换为字典.
完整代码如下:
protocol DictionaryConvertible {
/// 将实体转换为字典.
///
/// - Returns: 实体对应的字典
func toDictionary() -> [String: Any]
}
extension DictionaryConvertible {
func toDictionary() -> [String: Any] {
var dictionary = [String: Any]()
let mirrorSelf = Mirror(reflecting: self)
mirrorSelf.children.forEach({ label, value in
guard let key = label else { return }
if let array = value as? Array<DictionaryConvertible> {
dictionary[key] = array.map({ $0.toDictionary() })
} else if let convertible = value as? DictionaryConvertible {
dictionary[key] = convertible.toDictionary()
} else {
dictionary[key] = value
}
})
return dictionary
}
}
在实际使用时, 假设有如下实体类型:
struct MyInnerConsParam: Encodable, DictionaryConvertible {
let info: String
let price: Double
}
struct MyConsPostParam: Encodable, DictionaryConvertible {
let name: String
let num: Int
let inner: MyInnerConsParam
}
只要让实体遵守 DictionaryConvertible
协议即可获取字典转换能力:
let inner = MyInnerConsParam(info: "这个有信息", price: 33.3)
let param = MyConPostParam(name: "二狗", num: 11, inner: inner)
let dict = param.toDictionary()
print(dict)
打印结果如下:
["inner": ["info": "这个有信息", "price": 33.3], "name": "二狗", "num": 11]
网友评论