转载

初步学习 Mantle,修改使其支持任意嵌套 Array

昨晚,学习了一下 Mantle,参照的是这篇 blog ,首先感谢原文作者,基本熟悉了 Mantle 的使用方法,但那篇文章比较久远了,Mantle 现在对类型转换加入了 ErrorHandling 机制,如果你的 model 声明类型和解析 Json 得到的不一致,就会绑定失败,这点要特别注意。

原文还有一个坑要注意的是大家注意解析到的 json"Weather" 对应的 value 是一个 array

初步学习 Mantle,修改使其支持任意嵌套 Array

因此在 + (NSDictionary *)JSONKeyPathsByPropertyKey 方法中这样写是绑定不成功的,会导致整个 Model 为 nil

+ (NSDictionary *)JSONKeyPathsByPropertyKey {     return @{                 ...              @"conditionDescription": @"weather.description",                 ...              }; }

和K神 的讨论是 这种情况比较麻烦一些,需要创建一个新的 model,然后将他反序列化出来 。但我们还是想用 weather.description,毕竟这种最方便么。于是我打断点跑了一下 Mantle 的源码,发现主要是 "NSDictionary+MTLJSONKeyPath.h/NSDictionary+MTLJSONKeyPath.m" 这个类在控制着解析过程,我们来具体看一下:

#import "NSDictionary+MTLJSONKeyPath.h" #import "MTLJSONAdapter.h" @implementation NSDictionary (MTLJSONKeyPath) - (id)mtl_valueForJSONKeyPath:(NSString *)JSONKeyPath success:(BOOL *)success error:(NSError **)error {  //1. 传进来的 JSONKeyPath 其实就是 "weather.description",然后把他们每一部分都放进数组中  NSArray *components = [JSONKeyPath componentsSeparatedByString:@"."];  //2. 遍历 componets 数组  id result = self;  for (NSString *component in components) {   // Check the result before resolving the key path component to not   // affect the last value of the path.   if (result == nil || result == NSNull.null) break;   //3. 检查 result,不是字典对象就返回错误,并最终返回 nil    if (![result isKindOfClass:NSDictionary.class]) {    if (error != NULL) {     NSDictionary *userInfo = @{      NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid JSON dictionary", @""),      NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"JSON key path %1$@ could not resolved because an incompatible JSON dictionary was supplied: /"%2$@/"", @""), JSONKeyPath, self]     };     *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo];    }    if (success != NULL) *success = NO;    return nil;   }   // 4. 取出相应 key 所对应的值   result = result[component];  }  // 5. 成功并返回  if (success != NULL) *success = YES;  return result; } @end 

上面就是解析 "aaa.bbb" 这种语法的写法,我们观察一下 注释 3 就能发现该写法只支持 NSDictionary 对象,也就是说,只有 "aaa.bbb.ccc.ddd.eee" 中的 "aaa", "bbb", "ccc", "ddd", "eee" 都为 NSDictionary 对象时,才能解析成功,如果其中任意一个为数组对象就会解析失败,例如我们上面的 weather.description ,知道原因了,就好办多了,让我们来修改一下。

首先增加一个 NSArrayCategory NSArray+NTLJSONKeyPath ,内容和 NSDictionary+MTLJSONKeyPath 差不多,来看下实现:

#import "NSArray+NTLJSONKeyPath.h" #import "MTLJSONAdapter.h" @implementation NSArray (NTLJSONKeyPath) - (id)mtl_valueForJSONKeyPath:(NSString *)JSONKeyPath success:(BOOL *)success error:(NSError **)error {  NSUInteger count = self.count;  id result;  for (int i = 0; i < count; i++) {   // Check the result before resolving the key path component to not   // affect the last value of the path.   result = self[i];   if (result == nil || result == NSNull.null) break;   if (![result isKindOfClass:NSDictionary.class] && ![result isKindOfClass:[NSArray class]]) {    if (error != NULL) {     NSDictionary *userInfo = @{              NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid JSON dictionary", @""),              NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"JSON key path %1$@ could not resolved because an incompatible JSON dictionary was supplied: /"%2$@/"", @""), JSONKeyPath, self]              };     *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo];    }    if (success != NULL) *success = NO;    return nil;   }   if ([result isKindOfClass:NSDictionary.class]) {    result = result[JSONKeyPath];    if (result) break;   } else if ([result isKindOfClass:[NSArray class]]) {    result = [(NSArray *)result mtl_valueForJSONKeyPath:JSONKeyPath success:success error:error];   }  }  if (success != NULL) *success = YES;  return result; } 

代码也非常简单,如果遇到数组了,就遍历,如果是字典就根据当前 key,找出对应的 value,如果 value 还是数组,就继续递归执行。接着把 NSDictionary+MTLJSONKeyPath 小修改一下:

#import "NSDictionary+MTLJSONKeyPath.h" #import "NSArray+NTLJSONKeyPath.h" #import "MTLJSONAdapter.h" @implementation NSDictionary (MTLJSONKeyPath) - (id)mtl_valueForJSONKeyPath:(NSString *)JSONKeyPath success:(BOOL *)success error:(NSError **)error {  NSArray *components = [JSONKeyPath componentsSeparatedByString:@"."];  id result = self;  for (NSString *component in components) {   // Check the result before resolving the key path component to not   // affect the last value of the path.   if (result == nil || result == NSNull.null) break;   // 1. 这里增加了对 result 是否为数组的判断,如果是数组,就不要返回 nil 了   if (![result isKindOfClass:NSDictionary.class] && ![result isKindOfClass:[NSArray class]]) {    if (error != NULL) {     NSDictionary *userInfo = @{      NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid JSON dictionary", @""),      NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"JSON key path %1$@ could not resolved because an incompatible JSON dictionary was supplied: /"%2$@/"", @""), JSONKeyPath, self]     };     *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo];    }    if (success != NULL) *success = NO;    return nil;   }   //2. 判断 result 的类型,执行相应的方法   if ([result isKindOfClass:NSDictionary.class]) {    result = result[component];   } else if ([result isKindOfClass:[NSArray class]]) {    result = [(NSArray *)result mtl_valueForJSONKeyPath:component success:success error:error];   }  }  if (success != NULL) *success = YES;  return result; } 

只用修改上面两处,Binggo,一个可以处理 weather.description 的 Mantle 就改好啦,什么? weather.description 太简单,那我们试一下面的 'weather.pp.aa.dd'

初步学习 Mantle,修改使其支持任意嵌套 Array

解析结果:

初步学习 Mantle,修改使其支持任意嵌套 Array

是不是还不错呢,本篇的 demo 和 json 我都上传到 github 上了,可以自行试验,也许会有 bug,不过有什么问题可以给我留言

正文到此结束
Loading...