JSONModel源碼解析

JSONModel源碼解析

1、引言

    作移動端開發,解析網絡數據是必不可少的工做之一。iOS原生框架很早前就已經提供了將JSON數據直接映射成數組或者字典對象的方法,而且結合KVC,也能夠將字典數據直接賦值給對象。可是這種方式十分不靈活,例如若是網絡數據中的字段與咱們數據模型中的字段不一致,某些網絡數據的字段可能爲nil等等都須要開發者單獨的處理。使用JSOMModel能夠十分方便的處理映射過程當中的各類狀況。json

2、JSOMModel類概覽

    平時在使用JSOMModel框架時,每每只會用到JSOMModel這一個類,其實JSOMModel中還封裝了一套網絡請求邏輯,你能夠直接對某個對象調用請求來映射成爲數據模型。可是我建議儘可能將數據的請求和解析分開來作,這樣更利於請求的維護(在新的JSOMModel版本中,也將有關網絡請求的部分標記爲了棄用)。JSOMModel功能十分強大,代碼量卻並不特別多,結構邏輯也較爲清晰。其中類的關係和結構以下圖表示。數組

如上圖所示,其中網絡相關模塊已經棄用,而且也不是JSONModel的核心模塊,不在本次博客的探討範圍以內。JSONModelError定義了許多錯誤類型,主要用來當請求或數據解析異常時進行拋出,須要注意,JSONModel定義的本身的log函數,其只會在模擬器運行時進行打印。網絡

3、JSONModelClassProperty類的意義

    將網絡數據映射爲Model模型的實質便是對Model對象中屬性的賦值,在JSONModel中,類的屬性被抽象爲JSONModelClassProperty對象,這個對象中封裝這此屬性的相關信息(經過runtime來動態生成)。JSONModelClassProerty類中的屬性意義以下:app

@interface JSONModelClassProperty : NSObject
//已經棄用  這個用來標識當前屬性是不是對象的主鍵 用來進行數據模型的比較
@property (assign, nonatomic) BOOL isIndex DEPRECATED_ATTRIBUTE;
//屬性名
@property (copy, nonatomic) NSString *name;
//屬性類型
@property (assign, nonatomic) Class type;
//屬性結構體名稱 基本數據類型的屬性 會被抽象成結構體
@property (strong, nonatomic) NSString *structName;
//屬性遵照的協議名
@property (copy, nonatomic) NSString *protocol;
//當前屬性是不是可選屬性 若是是 在解析時容許這個屬性值爲nil
@property (assign, nonatomic) BOOL isOptional;
//是不是標準的json數據,若是是則不用再調用數據轉換的方法
@property (assign, nonatomic) BOOL isStandardJSONType;
//當前屬性是不是可變的 若是是 則會建立可變對象
@property (assign, nonatomic) BOOL isMutable;
//自定義的 屬性get函數
@property (assign, nonatomic) SEL customGetter;
//自定義的屬性 set函數
@property (strong, nonatomic) NSMutableDictionary *customSetters;
@end

4、關於屬性映射器JSONKeyMapper

    簡單理解,JSONKeyMapper屬性映射器的做用就是用來制定在數據解析時所遵循的規則。最理想的狀況是JSON數據與咱們要解析成的數據模型徹底對應,例如:框架

JSON數據:函數

{ "firstName":"Bill" , "lastName":"Gates" }

數據Model:atom

@interface MyOnject : NSObject

@property(nonatomic,strong)NSString * firstName;

@property(nonatomic,strong)NSString * lastName;

@end

然而在實際的開發中,這種完美的狀況卻不多出現,咱們更多遇到的是,JSON數據中某些字段可能有也可能無,數據Model中須要增長些本地字段,JSON數據和Model的某些字段名稱可能不一致。更加複雜一點,咱們能夠Model的某個屬性是另外一個Model。或者某個屬性是數組,數組中存放的是另外一種Model。spa

    JSONKeyMapper接口定義以下:調試

//經過字典來建立映射器  字典的鍵爲數據Model的屬性名  值爲JSOM數據的屬性名 
- (instancetype)initWithModelToJSONDictionary:(NSDictionary *)toJSON;

//經過block來創建映射關係 block的定義以下,其中會將JSOM數據的屬性名傳入 須要返回要對應Model的屬性名
/*
typedef NSString *(^JSONModelKeyMapBlock)(NSString *keyName);
*/ 
- (instancetype)initWithModelToJSONBlock:(JSONModelKeyMapBlock)toJSON;
//建立一個 將如下劃線分割的命名鍵 轉換成駝峯命名 例如 first_name => firstName
+ (instancetype)mapperForSnakeCase;
//建立一個 將首字母大寫的明明鍵 轉換成駝峯  例如 FirstName => firstName
+ (instancetype)mapperForTitleCase;

5、核心數據模型類JSONModel

    JSONModel框架中最核心的類JSONModel類,其中代碼大約有1400行,除了一些調試,複寫和提供方便功能的代碼外,核心代碼在800行左右。首先,其頭文件中聲明瞭幾個協議,以下:code

@protocol Index
@end
@protocol Ignore
@end
@protocol Optional
@end

須要注意,這些協議裏面都沒有約定任何方法,它們也不會用來實現的,其做爲屬性的一種標記,例如將屬性添加Ignore協議,則JSONModel不會對這個屬性進行解析,使用這種方式來進行本地數據的管理,例如:

@interface MyOnject : JSONModel

@property(nonatomic,strong)NSString * firstName;

@property(nonatomic,strong)NSString * lastName;
//這個屬性是本地拼接 使用
@property(nonatomic,strong)NSString<Ignore> * fullName;

@end

Optional協議表示這個屬性是可選的,即JSON數據中若是有這個屬性就解析,若是沒有就跳過。Index協議標記這個屬性是當前對象的主鍵,已經棄用。

    有了這3個協議,在聲明屬性時,咱們能夠十分容易的設定他們的解析規則,在JSONModel中,協議除了能夠用來規定解析規則外,還能夠用來指定自定義數據類型的解析,只是咱們須要本身定義一個協議,名稱與自定義類名一致,示例以下:

#import "JSONModel.h"

@protocol Address
@end

@interface Address:JSONModel

@property(nonatomic,strong)NSString * info;

@end

@interface MyObject : JSONModel

@property(nonatomic,strong)NSString<Optional> * firstName;

@property(nonatomic,strong)NSString * lastName;

@property(nonatomic,strong)NSArray<Address> * address;

@end

如上代碼因此,在解析數據時,會直接將address數組中賦值爲Address的對象,當前也能夠直接解析對象,例如:

@protocol Address
@end

@interface Address:JSONModel

@property(nonatomic,strong)NSString * info;

@end



@interface MyObject : JSONModel

@property(nonatomic,strong)NSString<Optional> * firstName;

@property(nonatomic,strong)NSString * lastName;

@property(nonatomic,strong)Address<Address> * address;

@end

須要注意,在Objective-C中,只有NSObject的子類能夠遵照協議,原始數據類型是不能遵照協議的,那麼對於相似BOOL,int這樣的屬性有沒有辦法設置他們的忽略解析或者可選解析呢,固然也能夠,咱們能夠經過重寫JSONModel中的一些函數來實現,這種方法更加通用,JSONModel類接口意義以下:

//將JSON字符串解析成數據模型對象
- (instancetype)initWithString:(NSString *)string error:(JSONModelError **)err;
- (instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err;
//將數據模型對象轉換成JSON字符串
- (NSString *)toJSONString;
//將數據模型對象轉換成JSON數據
- (NSData *)toJSONData;
//將數據模型對象中的某些鍵組合成JSON字符串
- (NSString *)toJSONStringWithKeys:(NSArray *)propertyNames;
//將數據模型對象中的某些鍵組合成JSON數據
- (NSData *)toJSONDataWithKeys:(NSArray *)propertyNames;

//重寫這個函數 來設置解析時使用的屬性映射器
+ (JSONKeyMapper *)keyMapper;
//重寫這個函數 來設置某個屬性是不是可選的
+ (BOOL)propertyIsOptional:(NSString *)propertyName;
//重寫這個函數 來設置某個屬性是不是忽略的
+ (BOOL)propertyIsIgnored:(NSString *)propertyName;
//重寫這個函數 來設置 若是某個屬性集合中是一個自定義對象或自己是自定義對象 設置此對象的類
+ (Class)classForCollectionProperty:(NSString *)propertyName;

JSONModel的源碼這裏就不在列舉,其首先在類load函數中進行靜態數據的加載,所支持的原生類型和基礎數據類型的定義等。在對象的初始化方法中,首先使用runtime獲取全部的屬性和屬性的修飾內容,所謂修飾內容,便是指屬性名稱,類型,所遵照的協議,以及是否忽略,是否可選,是不是主鍵等內容(過程當中會使用到屬性映射器keyMapper進行轉化),將其抽象成JSONModelClassProperty對象。後面在解析時,會根據JSONModelClassProperty中的自定義setter和其餘信息進行賦值。

相關文章
相關標籤/搜索