Mantle源码学习(二)

MTLJSONSerializing

该协议定义了数据转换之间的规则。需要MTLModel子类去实现。

// 该方法提供属性key与JSON的key path之间的对应关系。注意:如果一个key的对应关系没有被该函数返回,则不将参与JSON序列化。
+ (NSDictionary *)JSONKeyPathsByPropertyKey;
// 可选,通过Model的key获取NSValueTransformer,该方法基本被 +(NSValueTransformer *)key+JSONTransformerForKey;方法替代。
+ (NSValueTransformer *)JSONTransformerForKey:(NSString *)key;
// 指定转换的对象的类型。可选
+ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary;
MTLJSONAdapter

该类属于数据转换的核心类,转换的过程中该函数需要用户手动调用。

// 该函数提供了讲JSON转为对象的功能。
+ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error

该函数的流程:

  • 首先要初始化一个MTLJSONAdapter对象
MTLJSONAdapter *adapter = [[self alloc] initWithModelClass:modelClass];

该初始化的方法中,主要是检查JSONKeyPathsByPropertyKey中定义的合法性,缓存key的对应关系。同时通过以下代码:

_valueTransformersByPropertyKey = [self.class valueTransformersForModelClass:modelClass];

来对每一个key对应的NSValueTransformer(数据解析转换方法)进行缓存,缓存在一个字典中。这些方法有的是通过Model中来。以下几种是Model中实现的转换方式的几个例子,这种函数的格式是:key的name+JSONTransformer:

// NSURL的转换
+ (NSValueTransformer *)HTMLURLJSONTransformer {
    return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}

// 特定key-value转换。通过一个Map
+ (NSValueTransformer *)stateJSONTransformer {
    return [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{
                                                                           @"open": @(GHIssueStateOpen),
                                                                           @"closed": @(GHIssueStateClosed)
                                                                           }];
}

//对象的转换(Model中包含Model),MTLRecursiveUserModel为对象类型)
+ (NSValueTransformer *)ownerJSONTransformer {
    return [MTLJSONAdapter dictionaryTransformerWithModelClass:MTLRecursiveUserModel.class];
}

// 数组的转换(Model中包含数组属性),MTLRecursiveUserModel为数组中数据的类型)
+ (NSValueTransformer *)usersJSONTransformer {
    return [MTLJSONAdapter arrayTransformerWithModelClass:MTLRecursiveUserModel.class];
}

其他普通的数据类型的转换可以直接使用默认的NSValueTransformer来进行。无需在Model中进行实现对应的方法。

  • 然后系统开始序列化数据。
    这一步调用以下代码:
[adapter modelFromJSONDictionary:JSONDictionary error:error];

这个函数中,首先要判断Model是否实现了 classForParsingJSONDictionary 这个函数实现,实现了的话就用得讲JSON转换为这个函数中返回的数据类型。

    if ([self.modelClass respondsToSelector:@selector(classForParsingJSONDictionary:)]) {
        Class class = [self.modelClass classForParsingJSONDictionary:JSONDictionary];
        // 处理异常问题
        if (class == nil) {
            if (error != NULL) {
                NSDictionary *userInfo = @{
                    NSLocalizedDescriptionKey: NSLocalizedString(@"Could not parse JSON", @""),
                    NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"No model class could be found to parse the JSON dictionary.", @"")
                };

                *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorNoClassFound userInfo:userInfo];
            }

            return nil;
        }

// 当该协议方法中返回的类和当前原有的类不相同的时候,首先得检查是否实现了MTLJSONSerializing协议、然后就要重新初始化一个MTLJSONAdapter对象,然后开始转换啦。并且直接就return,后面的代码就不管了。
        if (class != self.modelClass) {
            NSAssert([class conformsToProtocol:@protocol(MTLJSONSerializing)], @"Class %@ returned from +classForParsingJSONDictionary: does not conform to <MTLJSONSerializing>", class);

            MTLJSONAdapter *otherAdapter = [self JSONAdapterForModelClass:class error:error];

            return [otherAdapter modelFromJSONDictionary:JSONDictionary error:error];
        }
    }
    

如果以上协议没有实现。就开始数据解析了,整个过程如下

NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:JSONDictionary.count];

// 遍历所有的key,解析每一个key的数据
    for (NSString *propertyKey in [self.modelClass propertyKeys]) {
        id JSONKeyPaths = self.JSONKeyPathsByPropertyKey[propertyKey];

        if (JSONKeyPaths == nil) continue;

        id value;

        // 获取key对应的value
        if ([JSONKeyPaths isKindOfClass:NSArray.class]) {
            NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

            for (NSString *keyPath in JSONKeyPaths) {
                BOOL success = NO;
                id value = [JSONDictionary mtl_valueForJSONKeyPath:keyPath success:&success error:error];

                if (!success) return nil;

                if (value != nil) dictionary[keyPath] = value;
            }

            value = dictionary;
        } else {
            BOOL success = NO;
            value = [JSONDictionary mtl_valueForJSONKeyPath:JSONKeyPaths success:&success error:error];

            if (!success) return nil;
        }

        if (value == nil) continue;

        @try {
        
        // 获取key对应的NSValueTransformer,然后使用该NSValueTransformer去转换数据
            NSValueTransformer *transformer = self.valueTransformersByPropertyKey[propertyKey];
            if (transformer != nil) {
                // Map NSNull -> nil for the transformer, and then back for the
                // dictionary we're going to insert into.
                if ([value isEqual:NSNull.null]) value = nil;

                if ([transformer respondsToSelector:@selector(transformedValue:success:error:)]) {
                    id<MTLTransformerErrorHandling> errorHandlingTransformer = (id)transformer;

                    BOOL success = YES;
                    // 对于数组和对象都会重新new一个MTLJSONAdapter,重新执行上面这些步骤,然后对value进行序列化。
                    value = [errorHandlingTransformer transformedValue:value success:&success error:error];

                    if (!success) return nil;
                } else {
                    // 这种情况一般是普通数据类型
                    value = [transformer transformedValue:value];
                }

                if (value == nil) value = NSNull.null;
            }

            dictionaryValue[propertyKey] = value;
        } @catch (NSException *ex) {
            NSLog(@"*** Caught exception %@ parsing JSON key path \"%@\" from: %@", ex, JSONKeyPaths, JSONDictionary);

            // Fail fast in Debug builds.
            #if DEBUG
            @throw ex;
            #else
            if (error != NULL) {
                NSDictionary *userInfo = @{
                    NSLocalizedDescriptionKey: ex.description,
                    NSLocalizedFailureReasonErrorKey: ex.reason,
                    MTLJSONAdapterThrownExceptionErrorKey: ex
                };

                *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorExceptionThrown userInfo:userInfo];
            }

            return nil;
            #endif
        }
    }

// 使用KVC讲value设置给对应的key, modelWithDictionary是MTLModel中定义的类函数,最终还是会调用构造函数 (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;讲对应的数据通过KVC设置给对象,这样就完成了序列化功能。

    id model = [self.modelClass modelWithDictionary:dictionaryValue error:error];

以上就详细的讲解了JSON---->Model的过程。其他的复杂功能暂时没有关注

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 话不多言,直接进入正文,感想如下: 如船长所言,运营应该如何快速成长? 入行了三个多月,作为一名内容运营,整天都在...
    奔跑不止阅读 156评论 0 0
  • 应大家的要求,这次将过程图一并贴出来。谈不上什么教程~只是分享出来供大家讨论:)希望大家一起参与交流哦,么么哒。 ...
    疯木头阅读 1,087评论 18 34