美文网首页工具iOS 开发 iOS Developer
iOS - 写一个简单的JSON转Model库

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

作者: Resory | 来源:发表于2017-02-15 11:11 被阅读297次

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


    相关文章

      网友评论

      本文标题:iOS - 写一个简单的JSON转Model库

      本文链接:https://www.haomeiwen.com/subject/mrtxjttx.html