動(dòng)手寫個(gè) JSON-Model Mapping 庫

Swift 在 JSON解析方面有個(gè)比較有名的第三方庫——SwiftyJSON,之前我也一直用的它。雖然用著還不錯(cuò),但是它主要是為了避免手動(dòng)解析 JSON 數(shù)據(jù)時(shí)大量的解包操作,降低解包不當(dāng)導(dǎo)致 crash 的風(fēng)險(xiǎn),感覺主要是注重安全性,易用性方面還是差了點(diǎn)。它支持下標(biāo)操作,但畢竟是以字符串為鍵取值,IDE 不能自動(dòng)補(bǔ)全,不僅麻煩還容易寫錯(cuò),而且用下標(biāo)取到的值是JSON類型,一般還需要再進(jìn)行類型轉(zhuǎn)換,終究沒有操作一個(gè) Model 來得方便。所以我一直想寫一個(gè) JSON-Model 的映射器,我想要的效果是這樣的:

  • 定義一個(gè) Model :
class JSONModel {
    var error = ""
    var count = 0
    var posts = []
}
  • 發(fā)送網(wǎng)絡(luò)請(qǐng)求后取得數(shù)據(jù),然后直接轉(zhuǎn)換成 JSONModel :
let jsonModel = data => JSOMModel.self

就這點(diǎn)需求,換個(gè)動(dòng)態(tài)語言那根本不是事兒,哪怕在 C# 這樣的靜態(tài)語言中也能很簡(jiǎn)單地實(shí)現(xiàn),只要用到反射特性就行了。然而 Swift 的反射實(shí)在是太弱了,運(yùn)行期只能查看屬性卻不能給屬性賦值。這簡(jiǎn)直無解,我讀了一下 SwiftyJSON 的源碼希望能找點(diǎn)靈感,然后發(fā)現(xiàn) SwfityJSON 的流程是這樣的(以直接解析 NSData 數(shù)據(jù)為例):

  • 以一個(gè) NSData 類型的數(shù)據(jù)作為構(gòu)造器參數(shù)實(shí)例化一個(gè) JSON(一個(gè) struct ),在構(gòu)造器中調(diào)用 NSJSONSerialization.JSONObjectWithData(...)方法,如果 data 能被反序列化成一個(gè) AnyObject 類型的對(duì)象的話,就調(diào)用另一個(gè)構(gòu)造器,把這個(gè)對(duì)象賦值給實(shí)例屬性 object ,否則就給object賦一個(gè)NSNull()。

  • object是一個(gè)計(jì)算屬性,在給它賦值時(shí),會(huì)對(duì)它的類型進(jìn)行判斷,然后把它的類型信息存儲(chǔ)到實(shí)例屬性type中(type是一個(gè)自定義的枚舉類型,這個(gè)枚舉類型基本對(duì)應(yīng)了 Swift 中的幾種基本類型),最后把object的值進(jìn)行類型轉(zhuǎn)化后賦值給JSON中的一個(gè)特定類型的私有屬性,譬如是數(shù)組的話就賦值給rawArray,是字符串的話就賦值給rawString,等等。

  • 在獲取object時(shí)會(huì)先判斷實(shí)例屬性type,根據(jù)type的值返回對(duì)應(yīng)的 rawValue,譬如 type == .String的話,就返回 rawString。然后像arrayarrayValue這樣的都是計(jì)算屬性,array的話會(huì)先去判斷type是不是.Array,是就返回rawArray,否則返回nil,而arrayValue不會(huì)返回nil,若類型不匹配則返回一個(gè)空數(shù)組[]

別的當(dāng)然還有一些內(nèi)容,譬如自定義下標(biāo),實(shí)現(xiàn)各種協(xié)議(字符串字面量協(xié)議、判等協(xié)議、比較協(xié)議、打印協(xié)議等等),代碼很優(yōu)雅,但似乎沒有我想要的東西。

最終我覺得,用 Swfit 的原生語法應(yīng)該是辦不到了,只能借助于 OC 的 runtime。主要是要用到 KVC,這樣一來所有的 Model 都得繼承自 NSObject。我寫了個(gè) Demo,從聯(lián)網(wǎng)獲取數(shù)據(jù)到顯示數(shù)據(jù)的整個(gè)流程如下:

  • 先看看JSON數(shù)據(jù)的結(jié)構(gòu):
JSON.png
  • 定義兩個(gè)Model:
class JSONModel: NSObject {
    var error = ""
    var count = 0
    var posts = []
}
class PostModel: NSObject {
    var id = 0
    var date = NSDate()
    var name = ""
    var pic = ""
    var publishtime = ""
    var count = 0
    var excerpt = ""
}
  • 發(fā)送網(wǎng)絡(luò)請(qǐng)求(你可以使用 Alamore 或別的什么庫,我這邊是自己簡(jiǎn)單封裝了一下 NSURLSession 直接用了),然后將取得的數(shù)據(jù)先轉(zhuǎn)化成 JSONModel(直接使用=>符號(hào)),保存到實(shí)例屬性jsonModel中:
getDataFromUrl(Constant.DemoAPI, method: .GET, parameter: nil) { data, error in
    if let jsonData = data {
        self.jsonModel = jsonData => JSONModel.self
    }
    
    if let httpError = error {
        print(httpError)
    }
}
  • jsonModel一旦被賦值就會(huì)刷新tableView,看一下配置cell的方法:
func configCell(cell: UITableViewCell, indexPath: NSIndexPath) -> UITableViewCell {
    
    if let model = jsonModel, post = model.posts[indexPath.section] => PostModel.self {
        cell.textLabel?.text = post.excerpt
    }
    
    return cell
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier(Constant.ReuseIdentifier, forIndexPath: indexPath)
    
    // Configure the cell...
    return configCell(cell, indexPath: indexPath)
    
}

也是用=>直接把posts數(shù)組中的元素都轉(zhuǎn)化為PostModel類型的實(shí)例了,然后直接cell.textLabel?.text = post.excerpt,就把我們想顯示的內(nèi)容放到cell里了。

顯示數(shù)據(jù).png

使用就是這么簡(jiǎn)單,只要新建一個(gè)NSObject的子類,屬性名保證跟 JSON 中的一致,并給各個(gè)屬性一個(gè)初始值。如果想另取屬性名也是可以的,用計(jì)算屬性就好了,譬如 Demo 中 publishtime 是不符合 Swift 屬性命名規(guī)范的,我們不去改原 Model,而是用一個(gè)擴(kuò)展:

extension PostModel {
    var publishTime: String {
        return publishtime
    }
}

這雖然不是很完美(因?yàn)?code>publishtime還在),但也湊合能用了。

轉(zhuǎn)換器主要是用到了反射( Mirror 實(shí)現(xiàn))和 KVC ,代碼就不貼了,大家可以去 Github直接看源碼,clone 下來跑一下 Demo 看看。要用到自己的項(xiàng)目中的話直接把 JSONModelMapper.swift文件或者連同HTTPManager.swift一起拖到項(xiàng)目中好了,因?yàn)閷?shí)在“超輕量級(jí)”(簡(jiǎn)陋……),我覺得這樣最方便了。

覺得還算有用的話隨手點(diǎn)個(gè) Star 可好……當(dāng)然有什么意見或建議的話歡迎指教。源碼在這里。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,190評(píng)論 4 61
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,540評(píng)論 19 139
  • 為了健康減肥,為了簡(jiǎn)單減肥,為了懶人減肥,相信很多朋友看到減肥兩字就把持不住瞄上兩眼。但是在現(xiàn)今這個(gè)高強(qiáng)度社會(huì)壓力...
    麥花魔法花園閱讀 16,785評(píng)論 0 7
  • 卜算子. 筆 腹內(nèi)藏江河,一瀉三千韻。搖落霞云日月輝,水墨乾坤潤(rùn)。 執(zhí)手走龍蛇,點(diǎn)染繁花盡。撇捺橫鉤釣流年,夢(mèng)醉空...
    不語不問閱讀 595評(píng)論 5 3

友情鏈接更多精彩內(nèi)容