iOS - 寫一個簡單的JSON轉Model庫

- 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。謝謝~


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

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

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