Swift JSON與模型轉換(HandyJSON封裝)


https://www.jianshu.com/p/25db8030dba2

 

2018.05.02 20:20:16字數 1,493閱讀 5,017
一 簡介
二 特性
三 安裝使用以及封裝
四 使用示例
五 總結
一 簡介

HandyJSON是一個用於Swift語言中的JSON序列化/反序列化庫。

與其他流行的Swift JSON庫相比,HandyJSON的特點是,它支持純swift類,使用也簡單。它反序列化時(把JSON轉換為Model)不要求Model從NSObject繼承(因為它不是基於KVC機制),也不要求你為Model定義一個Mapping函數。只要你定義好Model類,聲明它服從HandyJSON協議,HandyJSON就能自行以各個屬性的屬性名為Key,從JSON串中解析值。

HandyJSON目前依賴於從Swift Runtime源碼中推斷的內存規則,任何變動我們將隨時跟進。

二 特性
  • 序列化Model到JSON、從JSON反序列化到Model

  • 自然地以Model的屬性名稱作為解析JSON的Key,不需要額外指定

  • 支持Swift中大部分類型

  • 支持class、struct定義的Model

  • 支持自定義解析規則

  • 類型自適應,如JSON中是一個Int,但對應Model是String字段,會自動完成轉化

三 安裝使用以及封裝
3.1 安裝

我使用的是cocopod進行包引入管理,修改Prodfie文件,添加如下代碼:

pod 'HandyJSON', '~> 4.1.1'

官方說明(swift3.0用大於1.8.0版本的 swift4.0用4.1.1):

To use with Swift 3.x using >= 1.8.0 To use with Swift 4.0 using == 4.1.1 

實際使用過程中,1.8.0是有問題的,有個必現的bug,建議直接用4.1.1版本的,4.1如果安裝失敗,請更新你的cocopod到最新版

3.2 封裝

為了方便我們項目中的使用,我們一般都會在做一層封裝,方便庫的以后升級替換,以及方便日常業務邏輯的處理

JsonUtil.swift import UIKit import HandyJSON class JsonUtil: NSObject { /** * Json轉對象 */ static func jsonToModel(_ jsonStr:String,_ modelType:HandyJSON.Type) ->BaseModel { if jsonStr == "" || jsonStr.count == 0 { #if DEBUG print("jsonoModel:字符串為空") #endif return BaseModel() } return modelType.deserialize(from: jsonStr) as! BaseModel } /** * Json轉數組對象 */ static func jsonArrayToModel(_ jsonArrayStr:String, _ modelType:HandyJSON.Type) ->[BaseModel] { if jsonArrayStr == "" || jsonArrayStr.count == 0 { #if DEBUG print("jsonToModelArray:字符串為空") #endif return [] } var modelArray:[BaseModel] = [] let data = jsonArrayStr.data(using: String.Encoding.utf8) let peoplesArray = try! JSONSerialization.jsonObject(with:data!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] for people in peoplesArray! { modelArray.append(dictionaryToModel(people as! [String : Any], modelType)) } return modelArray } /** * 字典轉對象 */ static func dictionaryToModel(_ dictionStr:[String:Any],_ modelType:HandyJSON.Type) -> BaseModel { if dictionStr.count == 0 { #if DEBUG print("dictionaryToModel:字符串為空") #endif return BaseModel() } return modelType.deserialize(from: dictionStr) as! BaseModel } /** * 對象轉JSON */ static func modelToJson(_ model:BaseModel?) -> String { if model == nil { #if DEBUG print("modelToJson:model為空") #endif return "" } return (model?.toJSONString())! } /** * 對象轉字典 */ static func modelToDictionary(_ model:BaseModel?) -> [String:Any] { if model == nil { #if DEBUG print("modelToJson:model為空") #endif return [:] } return (model?.toJSON())! } } 

說明:這里我封裝了5個方法,Json轉對象,Json轉數組對象,字典轉對象,對象轉JSON,對象轉字典,基本覆蓋了我們日常開發的常用操作,與這個工具類相對應的,還有一個公共的基礎module類

BaseModel.swift import UIKit import HandyJSON class BaseModel: HandyJSON { // var date: Date? // var decimal: NSDecimalNumber? // var url: URL? // var data: Data? // var color: UIColor? required init() {} func mapping(mapper: HelpingMapper) { //自定義解析規則,日期數字顏色,如果要指定解析格式,子類實現重寫此方法即可 // mapper <<< // date <-- CustomDateFormatTransform(formatString: "yyyy-MM-dd") // // mapper <<< // decimal <-- NSDecimalNumberTransform() // // mapper <<< // url <-- URLTransform(shouldEncodeURLString: false) // // mapper <<< // data <-- DataTransform() // // mapper <<< // color <-- HexColorTransform() } } 

說明:封裝的基礎model類。開發中,我們自定義的model繼承此model即可省去了每次都要引入 “import HandyJSON” ,以及每次都要實現“required init() {}”方法,子類如果要自定義解析規則,重寫mapping方法即可

四 使用示例

基於以上封裝的類我們實現幾個示例做具體說明,主要有以下幾個示例:

  1. Json轉模型 (常用)
  2. Json數組轉模型 (常用)
  3. 字典轉模型
  4. 模型轉Json
  5. 模型轉字典
  6. json與嵌套的模型相互轉換
  7. 特殊類型字段轉換,日期類型,數字類型,顏色
1. Json轉模型 (常用)
JsonToModel.swift import UIKit class JsonToModel: BaseModel { var id :Int? var color:String? var name:String? } 
 fileprivate func jsonTomodel(){ let jsonString = "{\"id\":12345,\"color\":\"black\",\"name\":\"cat\"}" let model:JsonToModel = JsonUtil.jsonToModel(jsonString,JsonToModel.self) as! JsonToModel print(model.name as Any) print(model.color as Any) print(model.id as Any) } 

說明:調用jsonToModel(_ jsonStr:String,_ modelType:HandyJSON.Type),傳入兩個參數,第一個參數是要轉換的Json字符串,第二個參數是要轉換的model類的class,因為公共類中統一返回的都是BaseModel類型,所以這里要調用 as! 轉換成具體的子類類型。

2. Json數組轉模型 (常用)
JsonArrayToModel.swift import UIKit class JsonArrayToModel: BaseModel { var name:String? var id :String? } 
//json數組轉模型 fileprivate func jsonArrayTomodel() { let jsonArrayString: String = "[{\"name\":\"Bob\",\"id\":\"1\"}, {\"name\":\"Lily\",\"id\":\"2\"}, {\"name\":\"Lucy\",\"id\":\"3\"}]" let cats = JsonUtil.jsonArrayToModel(jsonArrayString, JsonArrayToModel.self) as! [JsonArrayToModel] for model:JsonArrayToModel in cats { print(model.name as Any) } } 

說明:調用jsonArrayToModel(_ jsonArrayStr:String, _ modelType:HandyJSON.Type)傳入兩個參數,第一個參數是要轉換的數組型Json字符串,第二個參數是要轉換的model類的class,返回值是一個數組,因為公共類中統一返回的都是BaseModel類型,所以這里要調用 as! 轉換成具體的子類類型數組。

3 字典轉模型
import UIKit class JsonToModel: BaseModel { var id :Int! var color:String? var name:String? } 
//字典轉模型 fileprivate func dicToModel() { var dict = [String: Any]() dict["id"] = 1.1 dict["color"] = "hello" dict["name"] = "李四" let model:JsonToModel = JsonUtil.dictionaryToModel(dict,JsonToModel.self) as! JsonToModel print(model.name as Any) print(model.color as Any) print(model.id as Any) } 

說明:調用dictionaryToModel(_ dictionStr:[String:Any],_ modelType:HandyJSON.Type),傳入兩個參數,第一個參數是要轉換的字典對象,第二個參數是要轉換的model類的class,因為公共類中統一返回的都是BaseModel類型,所以這里要調用 as! 轉換成具體的子類類型。

4. 模型轉Json
JsonToModel.swift import UIKit class JsonToModel: BaseModel { var id :Int! var color:String? var name:String? } 
//模型轉json fileprivate func modelToJson() { let model:JsonToModel = JsonToModel() model.color = "red" model.id = 100 model.name = "李四真" let modelTostring = JsonUtil.modelToJson(model) print(modelTostring) } 

說明:調用modelToJson(_ model:BaseModel?),傳入一個參數,傳入一個module對象,返回值是一個JSON字符串

5. 模型轉字典
JsonToModel.swift import UIKit class JsonToModel: BaseModel { var id :Int! var color:String? var name:String? } 
 //模型轉字典 fileprivate func modelTodiction() { let model:JsonToModel = JsonToModel() model.color = "red" model.id = 100 model.name = "李四" let modelTostring = JsonUtil.modelToDictionary(model) print(modelTostring["name"] as Any) } 

說明:調用modelToDictionary(_ model:BaseModel?)傳入一個參數,傳入一個module對象,返回值是一個字典對象,對於日常開發中,有時候后台只返回一個字段,比如返回一個成功信息字段,直接把返回的json串轉換成一個字典即可,沒必要再構建一個model去轉換。

6. json與嵌套的模型相互轉換
CombineModel.swift import UIKit class Composition: BaseModel { var aInt:Int? var aString:String? } class CombineModel: BaseModel { var aInt:Int? var comp1:Composition? var comp2:[Composition] = [] } 
//json與嵌套的模型相互轉換 fileprivate func jsonTocombilModel() { let model:CombineModel = CombineModel() model.aInt = 1001 let posModel1 = Composition() posModel1.aInt = 1 posModel1.aString = "趙六1" let posModel2 = Composition() posModel2.aInt = 2 posModel2.aString = "趙六2" let posModel3 = Composition() posModel3.aInt = 3 posModel3.aString = "趙六3" model.comp1 = posModel1 model.comp2.append(posModel2) model.comp2.append(posModel3) let modeString = JsonUtil.modelToJson(model) print(modeString) let model2 = JsonUtil.jsonToModel(modeString, CombineModel.self) print(model2) } 

說明:還是調用Json轉模型,模型轉Json的方法,本例子演示的是對象嵌套,對象里面可以嵌套對象,可以嵌套對象數組,平時我們開發經常遇到這種結構的Json串。

7. 特殊類型字段轉換,日期類型,數字類型,顏色
SpacialTypeModel.swift import UIKit import HandyJSON class SpacialTypeModel: BaseModel { var date: Date? var decimal: NSDecimalNumber? var url: URL? var data: Data? var color: UIColor? override func mapping(mapper: HelpingMapper) { mapper <<< date <-- CustomDateFormatTransform(formatString: "yyyy-MM-dd") mapper <<< decimal <-- NSDecimalNumberTransform() mapper <<< url <-- URLTransform(shouldEncodeURLString: false) mapper <<< data <-- DataTransform() mapper <<< color <-- HexColorTransform() } } 
// 特殊類型字段轉換,日期類型,數字類型,顏色 fileprivate func jsonToSpecialModel () { let object = SpacialTypeModel() object.date = Date() object.decimal = NSDecimalNumber(string: "1.23423414371298437124391243") object.url = URL(string: "https://www.aliyun.com") object.data = Data(base64Encoded: "aGVsbG8sIHdvcmxkIQ==") object.color = UIColor.blue let specailModelString = JsonUtil.modelToJson(object) print(object.toJSONString()!) // it prints: // {"date":"2017-09-11","decimal":"1.23423414371298437124391243","url":"https:\/\/www.aliyun.com","data":"aGVsbG8sIHdvcmxkIQ==","color":"0000FF"} let mappedObject:SpacialTypeModel = JsonUtil.jsonToModel(specailModelString, SpacialTypeModel.self) as! SpacialTypeModel print(mappedObject.date as Any) } 

說明:本例演示的是對於對象里含有特殊類型字段的轉換方法,主要注意點在構建model類里,我們要重寫父類mapping方法還要引入HandyJSON頭文件

五 總結

objective c有很多成熟的Json與模型轉換框架,對於swift就很少有類似的的框架,swift這方面比較成熟的框架swiftJSON,但是這個框架需要寫映射關系,感覺還是很不方便的,縱觀整個github,這個出自阿里的HandyJSON,不需要寫映射關系,方便靈活,功能多樣,使用起來還不錯。
最后附上:
本文DEMO:https://github.com/lerpo/HandyJSONDemo.git
HandyJSON文檔:https://github.com/alibaba/HandyJSON/blob/master/README_cn.md
HandyJSON原理https://yq.aliyun.com/articles/182517?spm=5176.11065265.1996646101.searchclickresult.705a7b7bA0G39T

 

使用HandyJSON導致的內存泄漏問題相關解決方法

https://www.cnblogs.com/zhanbaocheng/p/8125993.html

在移動開發中,與服務器打交道是不可避免的,從服務器拿到的接口數據最終都會被我們解析成模型,現在比較常見的數據傳輸格式是json格式,對json格式的解析可以使用原生的解析方式,也可以使用第三方的,我們的項目中使用的是阿里開源的一個swift編寫的解析框架--HandyJSON。

在使用過程中,使用instruments的Leak Checks工具對內存泄漏進行檢測時發現了這個框架導致了不少的內存泄漏,如圖1-1:
1-1 修復內存泄漏之前.png

這張圖是在APP進入首頁並將數據加載完畢時截取的,可以看到,HandyJSON一共導致了28個內存泄漏,如果把整個APP運行一遍,估計內存泄漏的個數會很多很多。

在HandyJSON的GitHub上找到了原因點擊前往,是UnsafeMutableRawPointer沒有釋放導致的,解決方法如下:

在Leak Checks工具中顯示了內存泄漏的了類名(Measuable)與方法名(_getBridgedPropertyList),我們找到HandyJSON中的這個類和方法,在里面進行釋放UnsafeMutableRawPointer就好了,在方法中加上圖1-2中的紅框中的代碼即可:

1-2 解決方法.png

添加完代碼之后,我們重新檢測內存泄漏的情況,發現如圖1-3所示:

1-3 內存泄漏解決之后.png

同樣是進入首頁並將數據加載完畢,我們發現,關於HandyJSON導致的內存泄漏已經沒有了。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM