模型转换工具Mantle, MJExtension, JSONModel的使用和异同

前端之家收集整理的这篇文章主要介绍了模型转换工具Mantle, MJExtension, JSONModel的使用和异同前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

现在大部分的项目都需要将服务器返回的JSON数据转换为Model再使用,手动转换不仅费时费力,还写了一堆重复代码,肯定是不科学的,一般都使用相应的工具来自动转换。目前接触的字典转模型工具有三种,Mantle,MJExtension,JSONModel,虽然他们做的事情都是一样的,但是使用方法区别还是蛮大的,以及在一些细节上的处理也是不同的。

Mantle的使用

简单的例子就不来了,可以直接到Github上面查看,这里上一个比较典型全面的例子。

@H_502_14@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (NSDictionary *)JSONDict {  if (_JSONDict == nil) {  _JSONDict = @{@"name" : [NSNull null],  "age" : @20,152)!important">"sex" : 0,152)!important">"login_date" : "1445136827",152)!important">"phone" : @{  "name" : "小明的iPhone",152)!important">"price" : 5000  }  "books" : @[  "西游记"},152)!important">"三国演义"}  ]  };  }  return _JSONDict; } 

对应模型的特点: 1、有NSNull对象,2、模型里面嵌套模型,3、模型里面有数组,数组里面有模型.

对应的模型如下:

@H_502_14@
18
19
20
21
22
23
.h typedef NS_ENUM(NSUInteger, Sex) {  SexMale,  SexFemale };  @interface BookForMantle : MTLModel <MTLJSONSerializing> property (nonatomic,210)!important">copy,210)!important">nullable) NSString *name; end  PhoneForMantle : assign) double price; UserForMantle : assign) NSInteger age; Sex sex; strong,210)!important">NSDate *loginDate; PhoneForMantle *phone; NSArray<BookForMantle *> *books; end 
@H_502_14@
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
m implementation PhoneForMantle  + (JSONKeyPathsByPropertyKey {  return "name",152)!important">"price"}; }  BookForMantle  + ("name"}; }  UserForMantle  // 该map不光是JSON->Model,Model->JSON也会用到 + ("age",152)!important">"sex",152)!important">"loginDate" : "login_date",152)!important">"phone",152)!important">"books"}; }  // 模型里面的模型 + (NSValueTransformer *)phoneTransformer {  return [MTLJSONAdapter dictionaryTransformerWithModelClass:[PhoneForMantle class]]; }  // 模型里面的数组 + (booksTransformer {  arrayTransformerWithModelClass:[BookForMantle // 时间 + (loginDateJSONTransformer {  MTLValueTransformer transformerUsingForwardBlock:^id(timeIntervalSince1970,210)!important">BOOL *success,210)!important">NSError *__autoreleasing *error) {  NSTimeInterval timeInterval = [timeIntervalSince1970 doubleValue];  date = [NSDate dateWithTimeIntervalSince1970:timeInterval];  date;  } reverseBlock:^date,210)!important">timeInterval = date.timeIntervalSince1970;  @(timeInterval).stringValue;  }]; }  end 

对应的解析方法:

@H_502_14@ @H_301_793@10
// 注意: Mantle不会自动转类型,如:String->Int,一旦类型不匹配,直接crash // Json->Model // 该方法调用key-key map方法 self.userForMantle = [modelOfClass:[UserForMantle class] fromJSONDictionary:JSONDict error:nil]; // 这种方式只是简单的使用KVC进行赋值。不会调用key-key map方法,要求属性和JSON字典中的key名称相同,否则就crash // self.userForMantle = [UserForMantle modelWithDictionary:self.JSONDict error:&error];  // Model -> JSON // 一旦有属性为nil,Mantle会转换成NSNull对象放到JSON字典中,这里有一个坑,使用NSUserDefault存储这样的JSON字典时,程序crash,原因是不可以包含NSNull对象。 NSDictionary *jsonDict = [JSONDictionaryFromModel:userForMantle nil]; 

JSOMModel的使用

仍旧使用上面的例子,对应的模型如下:

@H_502_14@
53
54
55
56
57
58
59
60
// BookForJsonModel protocol BookForJsonModel BookForJsonModel : JSONModel BookForJSONModel // 前面是服务器字段,后面是模型属性字段 + (JSONKeyMapper *)keyMapper {  return [[JSONKeyMapper alloc] initWithDictionary:"name"  }]; } end  // PhoneForJSONModel PhoneForJSONModel : PhoneForJSONModel + ("price"  }]; } // UserForJSONModel UserForJSONModel : PhoneForJSONModel *phone; // 注意协议 BookForJSONModel> *UserForJSONModel + ("loginDate",152)!important">"books"  }]; }  // 允许所有字段为空 + (BOOL)propertyIsOptional:(NSString *)propertyName {  YES; }  对应的解析方法

@H_502_14@
4
// JSON->Model UserForJSONModel *user = [[UserForJSONModel initWithDictionary:// Model->JSON dict = [user toDictionary]; 

JSONModel各方面都挺好的,唯一需要注意的地方是它归档的方式,它不是将对象归档,而是转换成字典再归档。

@H_502_14@
16
-(instancetype)initWithCoder:(NSCoder *)decoder {  NSString* json = [decoder decodeObjectForKey:"json"];   JSONModelError *error = nil;  self = [self initWithString:json error:&error];  JMLog("%@",[error localizedDescription]);  }  self; }  -(void)encodeWithCoder:(encoder {  [encoder encodeObject:toJSONString forKey:"json"]; } 

MJExtension的使用

这个的使用方式就不介绍了,GitHub上写的非常详细。这里主要说说我在用的项目中时遇到问题及解决方式。情况大致是这样的: 最开始项目中的模型统统继承自BaseModel类,解析方式都是自己挨个手动解析。还自定义了一些譬如时间戳转自定义日期类型的方法。在换到MJExtension时,没法对我们的自定义解析方式进行兼容,全部重写肯定是不现实的,只能做兼容。最后通过阅读MJExtension的源码,找到了一个突破口。在BaseModel里面对MJExtension里面的一个方法使用Method Swizzling进行替换。大致代码如下:

@H_502_14@
3
BaseModel.h 里面添加一个接口,子类可以覆盖 /// json->模型,转换完成之后调用,以便进行自定义配置。 - (mc_keyValuesDidFinishConvertingToObjectWithData:(id)data; 
@H_502_14@
28
m /* 替换方法: - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error; */ + (void)load {  static dispatch_once_t onceToken;  dispatch_once(&onceToken, ^{  Class class = [self class];   SEL originalSelector = selector(setKeyValues:context:error:);  swizzledSelector = mc_setKeyValues:Method originalMethod = class_getInstanceMethod(class,210)!important">originalSelector);  swizzledMethod = swizzledSelector);   BOOL success = class_addMethod(originalSelector,210)!important">method_getImplementation(swizzledMethod),210)!important">method_getTypeEncoding(swizzledMethod));  success) {  class_replaceMethod(swizzledSelector,210)!important">originalMethod),210)!important">originalMethod));  } else {  method_exchangeImplementations(originalMethod,210)!important">swizzledMethod);  }  }); }  - (mc_setKeyValues:(keyValues context:(NSManagedObjectContext *)context error:(NSError **)error {  MCDataModel *model = [mc_setKeyValues:context:if ([respondsToSelector:mc_keyValuesDidFinishConvertingToObjectWithData:)]) {  [mc_keyValuesDidFinishConvertingToObjectWithData:keyValues];  }  model; } 

这样在使用MJExtension将模型解析完成之后再调用mc_keyValuesDidFinishConvertingToObjectWithData: 将原始数据传递过去进行自定义配置。就可以很好的与老工程兼容了。

猜你在找的Json相关文章