- 0x00 寫在前面
JSON轉Model對于我們iOS開發(fā)來說有多重要就不贅述啦,而在Github上比較出名的有明杰老師的MJExtension,YY大神的YYModel,早些時候有Mantle等等。我也試著寫了個簡單的JSON轉Model庫RYModel,歡迎各位朋友點星和PR,以下是實現(xiàn)的過程,各位客官輕噴啊。
- 0x01 定個小目標
我們做這個JSON轉Model庫雖然簡單,但至少要包括以下功能和方法
才能滿足簡單的日常的開發(fā)。
| 功能 | 支持 |
|---|---|
| 基本類型賦值(bool,int,float...) | √ |
| Model包含其他Model | √ |
Model屬性名和JSON中key不同,下文簡稱這情況叫Mapping
|
√ |
| 方法 | 支持 |
|---|---|
| JSON->Model [Class ry_modelWithKeyValue] | √ |
| JSON String ->Model [Class ry_modelWithKeyValueString] | √ |
| JSONs -> Models [Class ry_modelsWithKeyValues] | √ |
| Model -> JSON [Class ry_modelToKeyValue] | √ |
| Moldes -> JSONs [Class ry_modelsToKeyValues] | √ |
- 0x02 分析過程
在分析過程前,建議各位先下載源碼RYModel并結合下面的流程圖分析
- ①首先我們得給NSObject添加個類別(這樣所有的類都可以使用),然后寫一個
+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic給予外部調(diào)用入口。 - 然后實例化一個類的對象
- (instancetype)ry_initWithKeyValue:(NSDictionary *)dic。 - ③得到實例化的對象后,就可以遍歷key查找Model是否存在改key
- (NSString *)ry_isExistKey:(NSString *)key,此方法中也包含了檢測mapping。 - ④得到合法的key后,我們還得判斷該key的相對應的類名是否為自定義的類
- (BOOL)ry_isSystemClass:(NSString *)key如果是自定義的類(Model包含Model的情況),則遞歸調(diào)用回+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic繼續(xù)解析,直到得到基本類型或者系統(tǒng)類后才用KVC方法賦值,整個解析流程就此結束。

RYModel流程圖.png
- 0x03 JSON->Model 部分源碼
- 初始化關鍵方法:
+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic
{
return [[self alloc] ry_initWithKeyValue:dic];;
}
- (instancetype)ry_initWithKeyValue:(NSDictionary *)dic
{
NSAssert([dic isKindOfClass:[NSDictionary class]], @"此數(shù)據(jù)為非字典,無法解析");
for (NSString *key in [dic allKeys]) {
NSString *tKey = [self ry_isExistKey:key];
if(tKey.length != 0){
// 存在key
[self ry_setKey:tKey withValue:dic[key]];
}else{
// 不存在key
NSLog(@"不存在該‘%@’字段",key);
}
}
return self;
}
- 判斷key是否存在重新
Mapping的情況,然后再檢查key是否能與Model的屬性匹配上,關鍵方法:
/**
是否存在key,如果有則返回key名(映射名)
@param key 字典key
@return 模型屬性名
*/
- (NSString *)ry_isExistKey:(NSString *)key
{
const char *aKey;
unsigned int count;
NSDictionary *mapDic;
// Model屬性有映射(mapping)
if([self respondsToSelector:@selector(ry_modelMapPropertyNames)]){
mapDic = [self ry_modelMapPropertyNames];
if(mapDic){
for (NSString *tKey in mapDic) {
if([key isEqualToString:mapDic[tKey]]){
return tKey;
}
}
}
}
// Model屬性無映射
aKey = [key UTF8String];
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (int i = 0 ; i < count; i++) {
const char *propertyName = property_getName(propertyList[i]);
if(strcmp(propertyName, aKey) == 0){
return [NSString stringWithUTF8String:aKey];
}
}
free(propertyList);
return nil;
}
- 判斷key對應的Model屬性是否是系統(tǒng)類,如果是用戶自定義的類,則取出相應key的類名,類名繼續(xù)調(diào)用
+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic,最后得出系統(tǒng)的類為止,再賦值即可,關鍵方法:
// 賦值
- (void)ry_setKey:(NSString *)key withValue:(id)value
{
id aValue;
if([self ry_isSystemClass:key]){
// 系統(tǒng)類
aValue = value;
}else{
// 自定義類(model嵌套model)
Class aClass = [self ry_getAttributeClass:key];
if([value isKindOfClass:[NSArray class]]){
// 嵌套的model數(shù)據(jù)是數(shù)組
aValue = [aClass ry_modelsWithKeyValues:value];
}else{
// 嵌套的model數(shù)據(jù)是字典
aValue = [aClass ry_modelWithKeyValue:value];
}
}
[self setValue:aValue forKey:key];
}
// key是否是系統(tǒng)的類
- (BOOL)ry_isSystemClass:(NSString *)key
{
Class aClass = [self ry_getAttributeClass:key];
if(aClass){
// 判斷key的類型是否是系統(tǒng)類
NSBundle *aBundle = [NSBundle bundleForClass:aClass];
if(aBundle == [NSBundle mainBundle]){
// 自定義的類
return NO;
}else{
// 系統(tǒng)類
return YES;
}
}else{
// 基本類型
return YES;
}
}
/**
獲取Model屬性的類名
eg: T@"RYCourse",&,N,V_course 獲取字符串中的'RYCourse'
@param key Model屬性對應的
@return Model屬性的類名
*/
- (Class)ry_getAttributeClass:(NSString *)key
{
Class aClass;
unsigned int count;
NSRange objRange;
NSRange dotRange;
NSString *aClassStr;
NSMutableString *aAttribute;
const char *att = "";
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (int i = 0 ; i < count; i++) {
const char *propertyName = property_getName(propertyList[i]);
NSString *tStr = [NSString stringWithUTF8String:propertyName];
if([key isEqualToString:tStr]){
att = property_getAttributes(propertyList[i]);
break;
}
}
free(propertyList);
aAttribute = [[NSMutableString alloc] initWithUTF8String:att];
objRange = [aAttribute rangeOfString:@"@"];
if(objRange.location != NSNotFound){
// key是對象,不是基本類型
dotRange = [aAttribute rangeOfString:@","];
aClassStr = [aAttribute substringWithRange:NSMakeRange(3, dotRange.location-1-3)];
aClass = NSClassFromString(aClassStr);
}else{
return nil;
}
return aClass;
}
- 0x04 最后
至此,JSON->Model就完成了。RYModel有剛剛小目標里提到的方法和功能的源碼,有興趣可以下載看看。如有問題或疑問可以留言或PR。謝謝~