搜索
您的当前位置:首页正文

<iOS 实践>一种实体转字典的思路

来源:二三娱乐

在日常开发活动中, 很多时候都需要将某个实体转换为字典, 而转换字典的方式有千千万万种, 这里介绍一种利用 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]
Top