Swift-實(shí)現(xiàn)字典模型轉(zhuǎn)換(一)

最近太忙太久沒寫文章了鞋囊,感覺有點(diǎn)不會寫了祖灰。
好了尿招,廢話不多說開始swift模型轉(zhuǎn)字典诀豁,字典轉(zhuǎn)模型的小小工具類編寫和思路窄刘。

思路:

以前 OC 是使用 runtime 獲取到 Model 里面的 key - value 然后用KVC進(jìn)行賦值。
那么Swift 也可以使用 Mirror 來獲取 Model 里面的 key - value 哦~舷胜,~娩践。

有了思路我現(xiàn)在就開始編寫,我在項(xiàng)目遇到的坑還有編寫遇到的坑烹骨,還有些沒解決的坑翻伺。??

以前 OC 中寫Model會繼承 NSObject ,按我的習(xí)慣來沮焕,在Swift我也習(xí)慣用NSObject來寫Model吨岭,所以現(xiàn)在我就用 NSObject來寫擴(kuò)展(extension)。

第一步峦树,使用Mirror動(dòng)態(tài)獲取Modelkey辣辫。
第二步,創(chuàng)建Model魁巩,使用KVC賦值急灭。
第三步,然后再轉(zhuǎn)Dictionary谷遂。
基本步驟就這樣子把葬馋。

模型:

class Person : NSObject {
  var name:String = ""
  var age:Int = 0
  var desc:String?
  var height:Double?
}

把大部分可能性都寫了下,好測試埋凯。
有些坑都在?上点楼,也就是Optional(可選值)???♂?。

擴(kuò)展:

第一步:

先來點(diǎn)簡單的白对,使用Mirror打印Model的 key 掠廓,type

extension NSObject {
    func variables() {
        let mirr = Mirror(reflecting: self)
        //Mirror 的 children 是一個(gè) (label: String?, value: Any) 元組類型,表示該類的所有屬性的名字和類型
        for case let (label,value) in mirr.children {
            if let key = label {
                let valueMirr = Mirror(reflecting: value)
                //subjectType 類型
                debugPrint("key -- \(key)  type -- \(valueMirr.subjectType)")
            }
        }
    }
}

終端打铀δ铡:

Test Case '-[ObjectConversionTests.ObjectConversionTests testModel]' started.
"key -- name  type -- String"
"key -- age  type -- Int"
"key -- desc  type -- Optional<String>"
"key -- height  type -- Optional<Double>"

然后就能看到寫 key type蟀瞧,這樣就好辦多了沉颂。知道key就可以進(jìn)行KVC了。

為了以后更可讀一點(diǎn)悦污,把一些常用基本類型進(jìn)行簡單的封裝成枚舉铸屉。

enum VariableType {
    case number  //數(shù)字
    case string  //字符串
    case bool  //布爾
    case array(String)  //數(shù)組 String 是對應(yīng)對象
    case dictionary  //字典
    case object  //對象
    case null  //NSNull
    case unknown  //無法解析數(shù)據(jù)
}

這一招我是模仿SwfitlyJson在做的,可以在Github搜索到??切端。

我現(xiàn)在就將數(shù)據(jù)進(jìn)行分類組合~~

typealias VarData = (String,VariableType,String,Any.Type)// key type modelStr class  數(shù)據(jù)結(jié)構(gòu)

extension NSObject {
    
    func variables() {
        let mirr = Mirror(reflecting: self)
        //Mirror 的 children 是一個(gè) (label: String?, value: Any) 元組類型彻坛,表示該類的所有屬性的名字和類型
        var keys = [VarData]()
        keys.append(contentsOf:self.categroy(mirr))
        debugPrint(keys)
    }
    
    func categroy(_ mirr: Mirror) -> [VarData] {
        var keys = [VarData]()
        for case let (label,value) in mirr.children {
            if let key = label {
                let valueMirr = Mirror(reflecting: value)
                var type = VariableType.unknown
                var str = ""
                if valueMirr.subjectType == String?.self || valueMirr.subjectType == String.self {
                    type = .string
                } else if valueMirr.subjectType == Int?.self ||
                    valueMirr.subjectType == Int.self ||
                    valueMirr.subjectType == Int64?.self ||
                    valueMirr.subjectType == Int64.self ||
                    valueMirr.subjectType == Float?.self ||
                    valueMirr.subjectType == Float.self ||
                    valueMirr.subjectType == Double?.self ||
                    valueMirr.subjectType == Double.self {
                    type = .number
                } else if valueMirr.subjectType == Bool?.self ||
                    valueMirr.subjectType == Bool.self {
                    type = .bool
                } else {
                    let typestr = "\(valueMirr.subjectType)"
                    if typestr.contains("Array") {
                        str = typestr
                        str = str.replacingOccurrences(of: "Optional<", with: "")
                        str = str.replacingOccurrences(of: "Array<", with: "")
                        str = str.replacingOccurrences(of: ">", with: "")
                        type = .array(str)
                    } else if typestr.contains("Dictionary") {
                        type = .dictionary
                    } else {
                        if valueMirr.subjectType is NSObject.Type {
                            type = .object
                        } else if valueMirr.subjectType is NSObject?.Type {
                            type = .object
                        }
                    }
                }
                keys.append((key,type,str,valueMirr.subjectType))
            }
        }
        return keys
    }
}
Test Case '-[ObjectConversionTests.ObjectConversionTests testModel]' started.
[("name", ObjectConversion.VariableType.string, "", Swift.String),
 ("age", ObjectConversion.VariableType.number, "", Swift.Int),
 ("desc", ObjectConversion.VariableType.string, "", Swift.Optional<Swift.String>),
 ("height", ObjectConversion.VariableType.number, "", Swift.Optional<Swift.Double>)]

數(shù)據(jù)改造后就能清晰的看清楚數(shù)據(jù)對應(yīng)的類型是啥了,是不是瞬間感覺友善很多了哈踏枣。

你們應(yīng)該會有點(diǎn)疑問為啥那么多基礎(chǔ)類型都做雙判斷昌屉,這就是坑點(diǎn)。

if valueMirr.subjectType == Double.self {
...
}

起初我的判斷是這樣的以上判斷茵瀑。

但是他識別不了Optional的變量间驮,所以我基本數(shù)據(jù)類型都會判斷Optional

if valueMirr.subjectType == Double.self
|| valueMirr.subjectType == Double?.self {
...
}

所以我才改成上面這樣子=马昨,=竞帽。

本來我是想過用字符串來進(jìn)行判斷的,后來想到這只是基本數(shù)據(jù)類型鸿捧,轉(zhuǎn)來轉(zhuǎn)去太麻煩了屹篓。

話題轉(zhuǎn)回去,接著繼續(xù)講轉(zhuǎn)換成模型的問題笛谦。

現(xiàn)在數(shù)據(jù)已經(jīng)有我需要的東東了抱虐。(key type)

現(xiàn)在做創(chuàng)建對象和賦值的操作。

extension NSObject {
...
    class func createObj(dict:Dictionary<String,Any>) -> Self {
        let obj = self.init()//創(chuàng)建對象
        //賦值操作
        return obj
    }
}

那么賦值操作步驟:
獲取key type關(guān)聯(lián)關(guān)系饥脑,上面我們就已經(jīng)獲取到了。
那么獲取后使用KVC進(jìn)行賦值懦冰。

extension NSObject {
...
   func variables() -> [VarData] {
        let mirr = Mirror(reflecting: self)
        //Mirror 的 children 是一個(gè) (label: String?, value: Any) 元組類型灶轰,表示該類的所有屬性的名字和類型
        var keys = [VarData]()
        keys.append(contentsOf:self.categroy(mirr))
        debugPrint(keys)
        return keys
    }
...
    /**根據(jù)我的項(xiàng)目需求 服務(wù)器key 和模型key 大小寫不等。刷钢。笋颤。
     * 此方法就過濾大小寫不等的問題。
     */
    private func ignoreCase(dict:Dictionary<String,Any>,key:String) -> Any? {
        if let value = dict[key] {
            return value
        } else if let value = dict[key.uppercased()] {
            return value
        } else if let value = dict[key.lowercased()] {
            return value
        } else {
            let dks = dict.keys.filter({ (key2) -> Bool in
                if key.uppercased() == key2.uppercased() {
                    return true
                } else if key.lowercased() == key2.lowercased() {
                    return true
                }
                return false
            })
            if dks.count == 1 {
                if let value = dict[dks.first!] {
                    return value
                }
            }
        }
        return nil
    }

    func pm_setValuesForKeys(_ dict:[String:Any]) {
        let vars = self.variables()
        for v in vars {
            switch v.1 {
            case .null:
                break
            case.unknown:
                break
            case .number:
                if let value = ignoreCase(dict:dict,key:v.0) as? String {
                    let decimal = NSDecimalNumber(string: value)
                    if decimal != NSDecimalNumber.notANumber {
                        self.setValue(decimal, forKey: v.0)
                    }
                } else {
                    if let value = ignoreCase(dict:dict,key:v.0) {
                        if !(value is NSNull) {
                            self.setValue(value, forKey: v.0)
                        }
                    }
                }
            case .string:
                if let value = ignoreCase(dict:dict,key:v.0) as? NSNumber {
                    self.setValue(value.stringValue, forKey: v.0)
                } else {
                    if let value = ignoreCase(dict:dict,key:v.0) as? String {
                        self.setValue(value, forKey: v.0)
                    } else {
                        if let value = ignoreCase(dict:dict,key:v.0) {
                            if !(value is NSNull) {
                                self.setValue(String(describing: ignoreCase(dict:dict,key:v.0)), forKey: v.0)
                            }
                        }
                    }
                }
                break
            default:
                if let value = ignoreCase(dict:dict,key:v.0) {
                    if !(value is NSNull) {
                        self.setValue(value, forKey: v.0)
                    }
                }
            }
        }
    }
   class func createObj(dict:Dictionary<String,Any>) -> Self {
        let obj = self.init() //創(chuàng)建對象
        obj.pm_setValuesForKeys(dict) //賦值操作
        return obj
    }
}

那么我們字典轉(zhuǎn)換模型大概就這樣完成了内地。

但是呢~~運(yùn)行后會奔潰伴澄,這是為什么呢。

[ObjectConversionTests.ObjectConversionTests testModel] : failed: caught "NSUnknownKeyException", "[<ObjectConversion.Person 0x6000000a1920> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key height."

在Swift4.0這句話說找不到對應(yīng)key阱缓。但是我利用Mirror獲取到key了呀非凌。。What荆针?
然后我發(fā)現(xiàn)Swift3.0 和 Swift4.0 模型的變量定義是不一樣的敞嗡。
Swift4.0是需要加@objc前綴修飾颁糟。

class Person : NSObject {
  @objc var name:String = ""
  @objc var age:Int = 0
  @objc var desc:String?
  @objc var height:Double = 0
}

然后在重新運(yùn)行,就運(yùn)行成功了喉悴。
在Swift3.0是不需要加的哦棱貌,具體為啥這里就不解釋了=,=箕肃。

做到這一步我們可以簡單的賦值了婚脱,但是還有Model里面變量Model的賦值還沒做。

下集分曉勺像。障贸。。剛好有活干了 T_T咏删。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惹想,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子督函,更是在濱河造成了極大的恐慌嘀粱,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辰狡,死亡現(xiàn)場離奇詭異锋叨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)宛篇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門娃磺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叫倍,你說我怎么就攤上這事偷卧。” “怎么了吆倦?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵听诸,是天一觀的道長。 經(jīng)常有香客問我蚕泽,道長晌梨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任须妻,我火速辦了婚禮仔蝌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荒吏。我一直安慰自己敛惊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布司倚。 她就那樣靜靜地躺著豆混,像睡著了一般篓像。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上皿伺,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天员辩,我揣著相機(jī)與錄音,去河邊找鬼鸵鸥。 笑死奠滑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妒穴。 我是一名探鬼主播宋税,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼讼油!你這毒婦竟也來了杰赛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤矮台,失蹤者是張志新(化名)和其女友劉穎乏屯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘦赫,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辰晕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了确虱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片含友。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖校辩,靈堂內(nèi)的尸體忽然破棺而出窘问,到底是詐尸還是另有隱情,我是刑警寧澤宜咒,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布南缓,位于F島的核電站,受9級特大地震影響荧呐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纸镊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一倍阐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逗威,春花似錦峰搪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽使套。三九已至,卻和暖如春鞠柄,著一層夾襖步出監(jiān)牢的瞬間侦高,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工厌杜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奉呛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓夯尽,卻偏偏與公主長得像瞧壮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子匙握,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容