打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
JSONModel源码阅读笔记

JSONModel是一个解析服务器返回的Json数据的库。

通常服务器传回的json数据要通过写一个数据转换模块将NSDictionary转换为Model,将NSString数据转换为Model中property的数据类型。

这样服务器如果要做修改,可能需要改两三个文件。

JSONModel的出现就是为了将这种解析工作在设计层面完成。

使用方法:参考连接

对其源码的核心部分JSONModel.m做了源码阅读,笔记如下:

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
函数中完成所有解析工作:如果有任何失误或者错误直接返回nil。

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err{    //1、做有效性判断(dict是不是空啊,dict是不是真是一个NSDictionary)    //check for nil input    if (!dict) {        if (err) *err = [JSONModelError errorInputIsNil];        return nil;    }    //invalid input, just create empty instance    if (![dict isKindOfClass:[NSDictionary class]]) {        if (err) *err = [JSONModelError errorInvalidData];        return nil;    }    //create a class instance    self = [super init];    if (!self) {                //super init didn't succeed        if (err) *err = [JSONModelError errorModelIsInvalid];        return nil;    }        //__setup__中通过调用__restrospectProperties建立类属性的映射表,并且存放在全局变量classProperties里面    //->__restrospectProperties中利用runtime function搞出属性列表:    //    ->获得属性列表class_copyPropertyList(得到objc_property_t数组)->对于每一个objc_property_t调用property_getName获得名称,property_getAttributes获得属性的描述(字符串)->通过解析字符串获得属性的类型、是否是Mutable、是否是基本的JSON类型等等    //    ->调用[class superclass]获得父类继续获取列表    //    ->列表保存在classProperties中备用    //->调用+keyMapper获得key转换列表,生成JSONKeyMapper对象存入keyMapper。    //do initial class setup, retrospec properties    [self __setup__];        //看看必传参数中是否在输入参数中都有。    //check if all required properties are present    NSArray* incomingKeysArray = [dict allKeys];    NSMutableSet* requiredProperties = [self __requiredPropertyNames];    NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];        //get the key mapper    JSONKeyMapper* keyMapper = keyMappers[__className_];        //transform the key names, if neccessary    if (keyMapper) {        //对比dict输入的keyName导入NSSet与keyMapper中JSONKeyMapper对象做keyName的转换。统一转换为对象的propertyname。        NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];        NSString* transformedName = nil;        //loop over the required properties list        for (NSString* requiredPropertyName in requiredProperties) {            //get the mapped key path            transformedName = keyMapper.modelToJSONKeyBlock(requiredPropertyName);                        //chek if exists and if so, add to incoming keys            if ([dict valueForKeyPath:transformedName]) {                [transformedIncomingKeys addObject: requiredPropertyName];            }        }                //overwrite the raw incoming list with the mapped key names        incomingKeys = transformedIncomingKeys;    }        //利用NSSet的isSubsetOfSet:将必传参数表与输入的keyName表对比。如果不是包含关系说明参数传的不够。    //check for missing input keys    if (![requiredProperties isSubsetOfSet:incomingKeys]) {        //get a list of the missing properties        [requiredProperties minusSet:incomingKeys];        //not all required properties are in - invalid input        JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self._className_, requiredProperties);                if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];        return nil;    }        //not needed anymore    incomingKeys= nil;    requiredProperties= nil;        //从对象的classProperties列表中循环到dict中取值:(赋值使用KVO操作的setValue:forKey:来做的,这样会直接调用setter函数赋值)    //loop over the incoming keys and set self's properties    for (JSONModelClassProperty* property in [self __properties__]) {        //对于每一个对象的property,通过keyMapper的转换找到对应dict property的dictKeyPath,找到值jsonValue。如果没有值,并且这个属性是Optional的就进行下一项property对比。        //convert key name ot model keys, if a mapper is provided        NSString* jsonKeyPath = property.name;                if (keyMapper) jsonKeyPath = keyMapper.modelToJSONKeyBlock( property.name );        //JMLog(@"keyPath: %@", jsonKeyPath);                //general check for data type compliance        id jsonValue = [dict valueForKeyPath: jsonKeyPath];                //check for Optional properties        if (jsonValue==nil && property.isOptional==YES) {            //skip this property, continue with next property            continue;        }                //对找到的值做类型判断,如果不是JSON应该返回的数据类型就报错。(注意:NSNull是可以作为参数回传的)        Class jsonValueClass = [jsonValue class];        BOOL isValueOfAllowedType = NO;                for (Class allowedType in allowedJSONTypes) {            if ( [jsonValueClass isSubclassOfClass: allowedType] ) {                isValueOfAllowedType = YES;                break;            }        }                if (isValueOfAllowedType==NO) {            //type not allowed            JMLog(@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass));            if (err) *err = [JSONModelError errorInvalidData];            return nil;        }                        //check if there's matching property in the model        //JSONModelClassProperty* property = classProperties[self.className][key];                //接着对property的属性与jsonValue进行类型匹配:        if (property) {                        //如果是基本类型(int/float等)直接值拷贝;            // 0) handle primitives            if (property.type == nil && property.structName==nil) {                                //just copy the value                [self setValue:jsonValue forKey: property.name];                                //skip directly to the next key                continue;            }                        //如果是NSNull直接赋空值;            // 0.5) handle nils            if (isNull(jsonValue)) {                [self setValue:nil forKey: property.name];                continue;            }            //如果是值也是一个JsonModel,递归搞JsonModel            // 1) check if property is itself a JSONModel            if ([[property.type class] isSubclassOfClass:[JSONModel class]]) {                                //initialize the property's model, store it                NSError* initError = nil;                id value = [[property.type alloc] initWithDictionary: jsonValue error:&initError];                if (!value) {                    if (initError && err) *err = [JSONModelError errorInvalidData];                    return nil;                }                [self setValue:value forKey: property.name];                                //for clarity, does the same without continue                continue;                            } else {                                //如果property中有protocol解析将jsonValue按照protocol解析,如NSArray<JsonModelSubclass>,protocol就是JsonModelSubclass                // 2) check if there's a protocol to the property                //  ) might or not be the case there's a built in transofrm for it                if (property.protocol) {                                        //JMLog(@"proto: %@", p.protocol);                                        //__transform:forProperty:函数功能:                    //    ->先判断下protocolClass是否在运行环境中存在,如不存在并且property是NSArray类型,直接报错。否则,直接返回。                    //    ->如果protocalClass是JsonModel的子类,                    //    ->如果property.type是NSArray                    //        ->判断一下是否是使用时转换                    //        ->如果为使用时转换则输出一个JSONModelArray(NSArray)的子类                    //        ->如果不是使用时转换则输出一个NSArray,其中的对象全部转换为protocalClass所对应对象                    //    ->如果property.type是NSDictionary                    //        ->将value转换为protocalClass所对应对象                    //        ->根据key存储到一个NSDictionary中输出                    jsonValue = [self __transform:jsonValue forProperty:property];                    if (!jsonValue) {                        if (err) *err = [JSONModelError errorInvalidData];                        return nil;                    }                }                                //如果是基本JSON类型(NSString/NSNumber)                // 3.1) handle matching standard JSON types                if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {                                        //如果是mutable的,做一份MutableCopy                    //mutable properties                    if (property.isMutable) {                        jsonValue = [jsonValue mutableCopy];                    }                                        //set the property value                    [self setValue:jsonValue forKey: property.name];                    continue;                }                                //如果property.type是NSArray                // 3.3) handle values to transform                if (                    //如果(类型没有匹配,并且jsonValue不为空)或者是Mutable的property(说明是特殊类型转换)                    (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))                    ||                    //the property is mutable                    property.isMutable                    ) {                                        //利用JSONValueTransformer找到源类型                    // searched around the web how to do this better                    // but did not find any solution, maybe that's the best idea? (hardly)                    Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];                                        //JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);                                        //用字符串拼出转换函数的名称字符串,到JSONValueTransformer中去搜索@SEL执行出正确类型                    //build a method selector for the property and json object classes                    NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",                                              (property.structName? property.structName : property.type), //target name                                              sourceClass]; //source name                    SEL selector = NSSelectorFromString(selectorName);                                        //check if there's a transformer with that name                    if ([valueTransformer respondsToSelector:selector]) {                                                //it's OK, believe me...#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warc-performSelector-leaks"                        //transform the value                        jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];#pragma clang diagnostic pop                                                [self setValue:jsonValue forKey: property.name];                                            } else {                                                // it's not a JSON data type, and there's no transformer for it                        // if property type is not supported - that's a programmer mistaked -> exception                        @throw [NSException exceptionWithName:@"Type not allowed"                                                       reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name]                                                     userInfo:nil];                        return nil;                    }                                    } else {                    //哪儿都不是的直接存起来                    // 3.4) handle "all other" cases (if any)                    [self setValue:jsonValue forKey: property.name];                }            }        }    }        //最后调用validate:看看结果是不是有效,没问题就返回了。    //run any custom model validation    NSError* validationError = nil;    BOOL doesModelDataValidate = [self validate:&validationError];        if (doesModelDataValidate == NO) {        if (err) *err = validationError;        return nil;    }        //model is valid! yay!    return self;}

程序亮点:
1、为了提高效率通过static的NSArray和NSDictionary进行解耦。
2、JSONValueTransformer实现了一个可复用的类型转换模板。
3、通过runtime function解析出property列表,通过property相关函数解析出名称,和Attributes的信息。
4、NSScanner的使用
5、NSSet的包含关系判断两个集合的交集
6、利用[NSObject setValue:forKey:]的KVO操作赋值,可以直接调用setter函数,并且可以赋nil到property中
7、适时给子类一个函数可以修改父类的一些行为

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Wrapper Classes for Run-Time Type Information (RTTI) UPDATED
iOS数据库离线缓存思路和网络层封装
关于友盟统计分析 iOS SDK——真正的独立第三方评测来了_U盟友盟统计分析论坛_友盟开发者社区
XcodeGhost 源代码
iOS开发网络编程之断点续传
软件测试技术之iOS 单元测试—逻辑测试
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服