iOS - 写一个简单的JSON转Model库

- 0x00 写在前面

JSON转Model对于我们iOS开发来说有多重要就不赘述啦,而在Github上比较出名的有明杰老师的MJExtension,YY大神的YYModel,早些时候有Mantle等等。我也试着写了个简单的JSON转Model库RYModel,欢迎各位朋友点星和PR,以下是实现的过程,各位客官轻喷啊。


- 0x01 定个小目标

我们做这个JSON转Model库虽然简单,但至少要包括以下功能和方法
才能满足简单的日常的开发。

功能 支持
基本类型赋值(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给予外部调用入口。
  • 然后实例化一个类的对象- (instancetype)ry_initWithKeyValue:(NSDictionary *)dic
  • ③得到实例化的对象后,就可以遍历key查找Model是否存在改key- (NSString *)ry_isExistKey:(NSString *)key,此方法中也包含了检测mapping
  • ④得到合法的key后,我们还得判断该key的相对应的类名是否为自定义的类- (BOOL)ry_isSystemClass:(NSString *)key如果是自定义的类(Model包含Model的情况),则递归调用回+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic继续解析,直到得到基本类型或者系统类后才用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]], @"此数据为非字典,无法解析");
    
    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属性是否是系统类,如果是用户自定义的类,则取出相应key的类名,类名继续调用+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic,最后得出系统的类为止,再赋值即可,关键方法:
// 赋值
  - (void)ry_setKey:(NSString *)key withValue:(id)value
{
    id aValue;
    
    if([self ry_isSystemClass:key]){
        // 系统类
        aValue = value;
    }else{
        // 自定义类(model嵌套model)
        Class aClass = [self ry_getAttributeClass:key];
        if([value isKindOfClass:[NSArray class]]){
            // 嵌套的model数据是数组
            aValue = [aClass ry_modelsWithKeyValues:value];
        }else{
            // 嵌套的model数据是字典
            aValue = [aClass ry_modelWithKeyValue:value];
        }
    }
    
    [self setValue:aValue forKey:key];
 }
// key是否是系统的类
 - (BOOL)ry_isSystemClass:(NSString *)key
{
    Class aClass = [self ry_getAttributeClass:key];
    
    if(aClass){
        // 判断key的类型是否是系统类
        NSBundle *aBundle = [NSBundle bundleForClass:aClass];
        if(aBundle == [NSBundle mainBundle]){
            // 自定义的类
            return NO;
        }else{
            // 系统类
            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。谢谢~


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容