? ? 在日常的iOS開發(fā)中,總會進(jìn)行數(shù)據(jù)的轉(zhuǎn)換,比如請求服務(wù)端獲取數(shù)據(jù),解析數(shù)據(jù),轉(zhuǎn)換成對應(yīng)的model,這個轉(zhuǎn)換過程比較繁瑣,重復(fù)工作較多,今天給大家介紹一個很好用的JSON轉(zhuǎn)換庫MJExtension。
? ? MJExtension是一套字典和模型之間互相轉(zhuǎn)換的超輕量級框架,利用runtime進(jìn)行數(shù)據(jù)之間的轉(zhuǎn)換,,使用簡單無侵入,使用門檻較低,下面簡單介紹下MJExtension的功能。
MJExtension的功能介紹
下面是MJExtension的功能,包括將JSON --> Model,Dictionary -->Model,Model -->JSON,JSON Array --> Model Array 等等功能。
JSON --> Model、Core Data Model
JSONString --> Model、Core Data Model
Model、Core Data Model --> JSON
JSON Array --> Model Array、Core Data Model Array
JSONString --> Model Array、Core Data Model Array
Model Array、Core Data Model Array --> JSON Array
MJExtension的使用
在MJExtension中,我們使用的api都在NSObject+MJKeyValue.h文件中,里面有詳細(xì)的注釋,這里就不多說了。下面我們就用微博的數(shù)據(jù)舉例子,進(jìn)行使用講解
1.簡單的字典 -> 模型(模型中的屬性都是基本類型)
1)首先我們看下字典轉(zhuǎn)模型,只需要一行代碼就可以,十分簡潔方便,這里需要注意下,model中的對應(yīng)屬性和字典中的key要相同,調(diào)用mj_objectWithKeyValues。


2)假如我們覺得MJUser中的屬性命名有些歧義,不是很好,這時候我們想改一下,比如字典中的name叫jack,可能對應(yīng)屬性叫firstname更好,這時候,我們就需要在字典轉(zhuǎn)模型的時候,進(jìn)行屬性名的替換,我們可以這么做,在MJUser.m文件中實現(xiàn)mj_replacedKeyFromPropertyName方法,方法如下圖所示。


3)如果在轉(zhuǎn)換過程中,我們只想對部分屬性,進(jìn)行轉(zhuǎn)換,比如,你只需要firstname和icon,name可以使用如下方法進(jìn)行設(shè)置,mj_allowedPropertyNames或者mj_ignoredPropertyNames,這兩個方法可以看做白名單和黑名單的方法。

在MJUser.m文件中實現(xiàn)mj_allowedPropertyNames方法,即可只轉(zhuǎn)換firstname和icon

4)如果在字典轉(zhuǎn)model的過程中,你需要改變firstname的值,在每個 firstname前面加上一段文字,比如:“jack”變成我是“我的名字是jack”,這時候可以使用mj_newValueFromOldValue方法,下面在MJUser.m文件中實現(xiàn)mj_newValueFromOldValue方法,每次進(jìn)行轉(zhuǎn)換的時候,都可以在firstname前加上“我的名字是”,或者還可以做一些其它的處理。

5)如果在字典轉(zhuǎn)換為model完成后,想做一些其它事情,可以使用mj_keyValuesDidFinishConvertingToObject方法進(jìn)行。
2.復(fù)雜的字典 -> 模型
? ? 除了上面的簡單字典轉(zhuǎn)模型,還有些復(fù)雜的模型,比如字典中包含字典和數(shù)組,這種情況,我們怎么處理呢???
1)首先我們看一下,下面這種數(shù)據(jù)結(jié)構(gòu),復(fù)雜的字典 -> 模型 (模型里面包含了模型)

這是一個字典嵌套字典的結(jié)構(gòu),它需要轉(zhuǎn)成一個模型嵌套模型的結(jié)構(gòu)

只需要調(diào)用mj_objectWithKeyValues方法即可,跟簡單模型使用方法一致,其它都不需要我們做,至于模型嵌套模型這種結(jié)構(gòu)的轉(zhuǎn)換時怎么實現(xiàn)的,我會在下一部分,講解原理的時候說到。
2)下面我們看一下,復(fù)雜的字典 -> 模型 (模型的數(shù)組屬性里面又裝著模型)


我們可以看到,上面MJStatusResult模型中的statuses屬性和ads屬性,都是一個array,而array中的每一個元素都是一個模型,比如statuses中是一個MJStatus模型,ads中是MJAd模型,在進(jìn)行MJStatusResult模型的轉(zhuǎn)換過程中,我們需要設(shè)置數(shù)組屬性的模型的類型,這時候我們需要用到mj_objectClassInArray方法,指定數(shù)組屬性中元素的模型類型,具體用法如下:

3.模型 -> 字典
模型轉(zhuǎn)字典主要有一下幾個方法,用法類似,這里就不多講了,大家可以看下源碼。

MJExtension的原理
MJExtension中類說明
在分析原理之前,我們先看下文件的MJExtension的文件組成,以及每個文件的作用。

1.MJExtension.h 只是引用了一些頭文件,方便管理。
2.MJExtensionConst.h和MJExtensionConst.m 聲明了一些宏定義,用來拋出異常和做判斷用的,同時聲明和定義了一些屬性類型。
3.MJFoundation.h和MJFoundation.m文件主要是用來判斷某個類是否是來自Foundation庫中的基本類型,在進(jìn)行字典和模型的轉(zhuǎn)換過程中,會根據(jù)這個類來判斷是否是模型嵌套模型的結(jié)構(gòu)。
4.MJProperty.h和MJProperty.m是一個記錄屬性信息的類,包含屬性的的所有信息,比如,屬性類型,名字,來源等等。
5.MJPropertyKey.h和MJPropertyKey.m提供一個根據(jù)屬性key,返回屬性值的方法。
6.MJPropertyType.h和MJPropertyType.m是記錄一個屬性的類型,比如MJProperty中屬性類型就是用MJPropertyType來記錄的,MJPropertyType類中包含類型標(biāo)識符,是否為基本數(shù)字類型,來源框架,是否支持KVC等等,在字典和模型轉(zhuǎn)換中,提供屬性類型信息。
7.NSObject+MJClass.h和NSObject+MJClass.m用來遍歷所有類,并進(jìn)行黑名單和白名單的管理。
8.NSObject+MJCoding.h和NSObject+MJCoding.m主要是對屬性值進(jìn)行歸檔和解檔,并且可以對屬性名字添加黑名單和白名單。
9.NSObject+MJKeyValue.h和NSObject+MJKeyValue.m類中提供了所有模型和字典之間轉(zhuǎn)換的接口,上面講的使用方法,都是來自與這個類的接口,我們需要熟悉里面的所有接口,以便可以更好的使用它。
10.NSObject+MJProperty.h和NSObject+MJProperty.m是遍歷模型所有成員,獲取模型中的屬性,用MJProperty類保存屬性,用于進(jìn)行字典和模型轉(zhuǎn)換。
11.NSString+MJExtension.h和NSString+MJExtension.m是用來進(jìn)行屬性名字轉(zhuǎn)換的類,比如講駝峰格式的類名,轉(zhuǎn)換為下劃線的命名格式。
MJExtension模型字典轉(zhuǎn)換過程
1.使用runtime 獲取模型的所有屬性 ,轉(zhuǎn)換成MJProperty屬性模型列表
2.根據(jù)MJProperty中的屬性類型,對屬性進(jìn)行處理,獲取屬性值。
3. 根據(jù)屬性名字,通過KVC對屬性賦值。
1.runtime如何獲取所有屬性
首先我們看下MJExtension的源碼,NSObject+MJProperty類中的+ (NSMutableArray *)properties方法。

1)首先使用runtime中的class_copyPropertyList方法,獲取類的所有屬性,class_copyPropertyList方法會返回一個objc_property_t類型的對象,這是一個結(jié)構(gòu)體,如下所示

objc_property是一個結(jié)構(gòu)體,是一個內(nèi)置的類型,與之關(guān)聯(lián)的還有一個objc_property_attribute_t,它是屬性的attribute,也就是其實是對屬性的詳細(xì)描述,包括屬性名稱、屬性編碼類型、原子類型/非原子類型等。它的定義如下:

2)然后遍歷每個成員,將objc_property_t中的屬性,轉(zhuǎn)換成MJProperty對象,轉(zhuǎn)換過程如下:

首先使用property_getName獲取屬性名,然后使用property_getAttributes獲取objc_property_attribute_t,為一個字符串的結(jié)果,里面包含屬性名稱、屬性編碼類型、原子類型/非原子類型等,比如:NSString類型的屬性,對應(yīng)的attrs為T@"NSString",C,N,V_icon。下面我解釋一下這個字符串對應(yīng)的意思:
T 表示屬性的類型 類型為基本對象類型和基本數(shù)據(jù)類型,基本對象類型的value為該對象類型名字 如NSArray、NSString、NSMutableDictionary 等
C 表示該屬性為copy ;為&表示屬性為strong;W表示屬性為weak;空 表示屬性為assgin
N表示為非原子屬性
V表示屬性的名字 此時value為加了下劃線的屬性名字
最后會根據(jù)property_getAttributes信息生成屬性對象的type。
2.根據(jù)MJProperty中的屬性類型,對屬性進(jìn)行處理,獲取屬性值。
根據(jù)屬性類型,對屬性進(jìn)行處理主要在- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 方法中進(jìn)行的,如下所示:


從上面代碼可以看出
1)通過valueInObject獲取屬性值value,
2)獲取到值以后,做了一些特殊處理
將value由可變對象轉(zhuǎn)換成不可變類型
如果value是模型,則繼續(xù)將value轉(zhuǎn)換成模型。
如果模型類型是NSString類型,會將屬性類型中的NSNumber和NSURL轉(zhuǎn)換為NSString。
如果模型類型是NSURL類型,會將屬性類型中NSString轉(zhuǎn)換為NSURL。
如果模型類型是NSNumber類型,會將屬性類型中NSString轉(zhuǎn)換為NSNumber。
如果模型類型是NSNumber類型,會將屬性類型中NSString轉(zhuǎn)換為NSNumber。
如果模型類型是BOOL類型,會將字符串和yes,true,no,false進(jìn)行匹配,轉(zhuǎn)換為BOOL類型
如果模型類型和屬性類型不匹配,會將value設(shè)置為nil。
3. 根據(jù)屬性名字,通過KVC對屬性賦值。
接下來,會使用屬性值value,為屬性賦值,如下所示:

首先會判斷是否支持KVC,如果支持會給屬性賦值。
到這里就講解完了MJExtension的使用和原理