上海快风信息科技有限公司

模型转换Mantle,JSONModel,MJExtension

2018-01-02  本文已影响68人  lihhm

模型转换Mantle,JSONModel,MJExtension,YYModel的使用

把JSON数据转换为模型,关于介绍什么不多说,作为一个基本程序猿不需要解释了吧。

1.Mantle的使用

Mantle

上面介绍的很详细,具体其他用法自行查看。

基本步骤:

1.下载Mantle

2.创建model时候必须继承:MTLModel

3.model必须实现协议:MTLJSONSerializing

4.重写协议的一个方法:

//使用Mantle必须实现的协议 KVO的形式

+(NSDictionary *)JSONKeyPathsByPropertyKey

5.如果model里面属性有嵌套model 或者 属性需要做转换等就实现

+(NSValueTransformer *)JSONTransformerForKey:(NSString *)key

6.映射model主要涉及的类:MTLJSONAdapter

基本原理:

1.获取 model 的属性--> JSONKeyPath 映射字典

2.获取 model 的属性列表

3.根据 model 的方法给网络请求中返回的 JSON 字典中的 value 做值类型转化操作

4.使用 KVC 把值赋给 model 的属性,完成操作

某个测试API

如果你打开了,那数据基本上就是这样,仅供测试使用,无其他用途。

使用火狐浏览器打开如下:

仅供测试API.png
(1).Mantle 要求所有的 Model 都要继承于 MTLModel 并实现 MTLJSONSerializing 协议

MantleModel.h

@class MantleDataModel,MantleFeedModel,MantleDislikeModel,MantleCommentCountModel,MantlePicsModel,MantleListModel;

@protocol MantleFeedModel <NSObject>
@end
@protocol MantleDislikeModel <NSObject>
@end
@protocol MantleListModel <NSObject>
@end

@interface MantleModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,assign)int status;
@property (nonatomic,copy)NSString *resTime;
@property (nonatomic,copy)NSString *uni;
@property (nonatomic,copy)NSString *localUni;
@property (nonatomic,strong)MantleDataModel *data;
@end

@interface MantleDataModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,assign)NSInteger isIntro;
@property (nonatomic,copy)NSString *feedDownType;
@property (nonatomic,assign)NSInteger feedLastIndex;
@property (nonatomic,strong)NSArray <MantleFeedModel>*feed;
@property (nonatomic,copy)NSString *downText;
@property (nonatomic,copy)NSString *lastTimestampString;
@end

@interface MantleFeedModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,copy)NSString *newsId;
@property (nonatomic,copy)NSString *longTitle;
@property (nonatomic,copy)NSString *title;
@property (nonatomic,copy)NSString *source;
@property (nonatomic,copy)NSString *link;
@property (nonatomic,copy)NSString *pic;
@property (nonatomic,copy)NSString *kpic;
@property (nonatomic,copy)NSString *intro;
@property (nonatomic,copy)NSString *pubDate;
@property (nonatomic,assign)NSInteger interestSwitch;
@property (nonatomic,strong)NSArray <MantleDislikeModel>*dislikeTags;
@property (nonatomic,copy)NSString *commentId;
@property (nonatomic,assign)NSInteger comment;
@property (nonatomic,strong)MantleCommentCountModel *commentCountInfo;
@property (nonatomic,copy)NSString *feedShowStyle;
@property (nonatomic,copy)NSString *category;
@property (nonatomic,strong)MantlePicsModel *pics;
@property (nonatomic,copy)NSString *recommendInfo;
@property (nonatomic,copy)NSString *articlePubDate;

@end

@interface MantleDislikeModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,assign)NSInteger commentStatus;
@property (nonatomic,assign)NSInteger qreply;
@property (nonatomic,assign)NSInteger show;
@property (nonatomic,assign)NSInteger all;
@property (nonatomic,assign)NSInteger praise;
@property (nonatomic,assign)NSInteger dispraise;
@end

@interface MantleCommentCountModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,assign)NSInteger commentStatus;
@property (nonatomic,assign)NSInteger qreply;
@property (nonatomic,assign)NSInteger show;
@property (nonatomic,assign)NSInteger all;
@property (nonatomic,assign)NSInteger praise;
@property (nonatomic,assign)NSInteger dispraise;
@end

@interface MantlePicsModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,strong)NSArray <MantleListModel>*list;
@property (nonatomic,assign)NSInteger total;
@end

@interface MantleListModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,copy)NSString *pic;
@property (nonatomic,copy)NSString *alt;
@property (nonatomic,copy)NSString *kpic;
@end

MantleModel.m

@implementation MantleModel
//必须实现
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{@"status":@"status",
             @"resTime":@"resTime",
             @"uni":@"uni",
             @"localUni":@"localUni",
             @"data":@"data"};
}

// 模型里面的模型
+ (NSValueTransformer *)dataTransformer {
    return [MTLJSONAdapter dictionaryTransformerWithModelClass:MantleDataModel.class];
}

//实现类似+<key>JSONTransformer的方法,来指定某个属性从字典里面取值后的类型(或怎么取值):
//SEL selector = MTLSelectorWithKeyPattern(key, "JSONTransformer");
//注意:属性名+JSONTransformer
+ (NSValueTransformer *)resTimeJSONTransformer
{
    return [self dateJSONTransformer];
}

//日期格式转换
+ (NSValueTransformer *)dateJSONTransformer
{
    return [MTLValueTransformer transformerUsingForwardBlock:^id(id value, BOOL *success, NSError *__autoreleasing *error) {
        if (success) {
            NSTimeInterval secs = [value doubleValue];
            return [NSDate dateWithTimeIntervalSince1970:secs];
        }else {
            return @([value timeIntervalSince1970]);
        }
    }];
}

@end

@implementation MantleDataModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{@"isIntro":@"isIntro",
             @"feedDownType":@"feedDownType",
             @"feedLastIndex":@"feedLastIndex",
             @"feed":@"feed",
             @"downText":@"downText",
             @"lastTimestamp":@"lastTimestamp"
             };
}

// 模型里面的数组
+ (NSValueTransformer *)feedTransformer {
    return [MTLJSONAdapter arrayTransformerWithModelClass:MantleFeedModel.class];
}

@end

@implementation MantleFeedModel

+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    NSMutableDictionary *mapping = [[NSDictionary mtl_identityPropertyMapWithModel:self] mutableCopy];
    return mapping;
}

@end

@implementation MantleDislikeModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    NSMutableDictionary *mapping = [[NSDictionary mtl_identityPropertyMapWithModel:self] mutableCopy];
    return mapping;
}

@end

@implementation MantleCommentCountModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    NSMutableDictionary *mapping = [[NSDictionary mtl_identityPropertyMapWithModel:self] mutableCopy];
    return mapping;
}

@end

@implementation MantlePicsModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    NSMutableDictionary *mapping = [[NSDictionary mtl_identityPropertyMapWithModel:self] mutableCopy];
    return mapping;
}

@end

@implementation MantleListModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    NSMutableDictionary *mapping = [[NSDictionary mtl_identityPropertyMapWithModel:self] mutableCopy];
    return mapping;
}

@end
(2).简单说明:

对JSONKeyPathsByPropertyKey上面写了两种,其中的MantleDataModel你可以利用JSONKeyPathsByPropertyKey把所有属性按字典列出来:

+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{@"isIntro":@"isIntro",
             @"feedDownType":@"feedDownType",
             @"feedLastIndex":@"feedLastIndex",
             @"feed":@"feed",
             @"downText":@"downText",
             @"lastTimestamp":@"lastTimestamp"
             };
}

或许你也可以直接写成这样:

 + (NSDictionary *)JSONKeyPathsByPropertyKey {
    NSMutableDictionary *mapping = [[NSDictionary mtl_identityPropertyMapWithModel:self] mutableCopy];
    mapping[@"lastTimestampString"] = @"lastTimestamp";
    return mapping;
}

利用属性名+JSONTransformer,对于dateJSONTransformer其中简单进行日期转换。

+ (NSValueTransformer *)dateJSONTransformer
{
    return [MTLValueTransformer transformerUsingForwardBlock:^id(id value, BOOL *success, NSError *__autoreleasing *error) {
        if (success) {
            NSTimeInterval secs = [value doubleValue];
            return [NSDate dateWithTimeIntervalSince1970:secs];
        }else {
            return @([value timeIntervalSince1970]);
        }
    }];
}

进行关键字替换:

+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    NSMutableDictionary *mapping = [[NSDictionary mtl_identityPropertyMapWithModel:self] mutableCopy];
    mapping[@"lastTimestampString"] = @"lastTimestamp";
    return mapping;
}

当你的 Model 里的所有属性的名字和 JSON 里的所有 key 的名字完全相同的时候,你就可以用这个方法直接生成一个 NSDictionary, 直接返回:

比如这个:

+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{@"status":@"status",
             @"resTime":@"resTime",
             @"uni":@"uni",
             @"localUni":@"localUni",
             @"data":@"data"};
}

你也可以直接这样:

 + (NSDictionary *)JSONKeyPathsByPropertyKey {
 return [NSDictionary mtl_identityPropertyMapWithModel:self];
 }
(3)其他

注意: Mantle不会自动转类型,如:String->Int, 一旦类型不匹配,直接crash。
Json->Model 该方法会调用key-key map方法。

self.userForMantle = [MTLJSONAdapter modelOfClass:[UserForMantle class] fromJSONDictionary:self.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 = [MTLJSONAdapter JSONDictionaryFromModel:self.userForMantle error:nil];

优点:

1.修改字段映射时相当简便

2.扩展时相对方便

3.可以实现更多复杂的映射关系和数值转换

4.实现了NSCopying和NSCoding协议,可以轻松序列化

缺点:

1.基于运行时属性映射,对性能有一定影响

2.有部分容错处理需要自行解决

其他等等可以自行查看Mantle

2.JSONModel

JSONModel

【对应上面测试API,数据仅供测试使用】:

(1). 所有Model类继承于JSONModel

JSONModel.h

@class dataModel,commentCountModel,picsModel;

@protocol feedModel <NSObject>
@end
@protocol dislikeModel <NSObject>
@end
@protocol commentCountModel <NSObject>
@end
@protocol picsModel <NSObject>
@end
@protocol listModel <NSObject>
@end

@interface BaseJsonModel : JSONModel

@property (nonatomic,assign)int status;
@property (nonatomic,copy)NSString *resTime;
@property (nonatomic,copy)NSString <Optional>*uni;
@property (nonatomic,copy)NSString *localUni;
@property (nonatomic,strong)dataModel *data;

@end

@interface dataModel : JSONModel
@property (nonatomic,assign)NSInteger isIntro;
@property (nonatomic,copy)NSString *feedDownType;
@property (nonatomic,assign)NSInteger feedLastIndex;
@property (nonatomic,strong)NSArray<feedModel>*feed;
@property (nonatomic,copy)NSString *downText;
@property (nonatomic,copy)NSString *lastTimestamp;
@end

@interface feedModel : JSONModel
@property (nonatomic,copy)NSString *newsId;
@property (nonatomic,copy)NSString *longTitle;
@property (nonatomic,copy)NSString *title;
@property (nonatomic,copy)NSString *source;
@property (nonatomic,copy)NSString *link;
@property (nonatomic,copy)NSString *pic;
@property (nonatomic,copy)NSString *kpic;
//@property (nonatomic,copy)NSString *intro;
@property (nonatomic,copy)NSString *des;
@property (nonatomic,copy)NSString *pubDate;
@property (nonatomic,assign)NSInteger interestSwitch;
@property (nonatomic,strong)NSArray <dislikeModel>*dislikeTags;
@property (nonatomic,copy)NSString *commentId;
@property (nonatomic,assign)NSInteger comment;
@property (nonatomic,strong)commentCountModel *commentCountInfo;
@property (nonatomic,copy)NSString *feedShowStyle;
@property (nonatomic,copy)NSString *category;
@property (nonatomic,strong)picsModel *pics;
@property (nonatomic,copy)NSString *recommendInfo;
@end

@interface dislikeModel : JSONModel

@end

@interface commentCountModel : JSONModel
@property (nonatomic,assign)NSInteger commentStatus;
@property (nonatomic,assign)NSInteger qreply;
@property (nonatomic,assign)NSInteger show;
@property (nonatomic,assign)NSInteger all;
@property (nonatomic,assign)NSInteger praise;
@property (nonatomic,assign)NSInteger dispraise;
@end

@interface picsModel : JSONModel
@property (nonatomic,strong)NSArray <listModel>*list;
@property (nonatomic,assign)NSInteger total;
@end

@interface listModel : JSONModel
@property (nonatomic,copy)NSString *pic;
@property (nonatomic,copy)NSString *alt;
@property (nonatomic,copy)NSString *kpic;
@end

JSONModel.m

@implementation BaseJsonModel

@end
@implementation dataModel

@end
@implementation feedModel
+ (JSONKeyMapper *)keyMapper {
    return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:
            @{
              @"des":@"intro",
              }];
}
@end
@implementation dislikeModel

@end
@implementation commentCountModel

@end
@implementation picsModel

@end
@implementation listModel

@end

(2).简单说明:

关键值替换

+ (JSONKeyMapper *)keyMapper {
    return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:
            @{
              @"des":@"intro",
              }];
}

忽略属性

 @property (strong, nonatomic) NSString<Ignore>* qreply;

设置某个字段为可选(为空)

 @property (strong, nonatomic) NSString<Optional>* qreply;

设置所有的属性为可选(为空)

+(BOOL)propertyIsOptional:(NSString*)propertyName
 {
 return YES;
 }

自动把下划线方式的命名转为驼峰命名属性,大小写转换

 {
 "list_name": wang,
 "list_pic" : @"11",
 }

生成模型:

 @interface ListModel : JSONModel
 
 @property (copy, nonatomic) int listName;
 @property (assign, nonatomic) float listPic;
 
 @end
 
 @implementation ListModel
 
 +(JSONKeyMapper*)keyMapper
 {
 return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
 }

其他等等可以自行查看JSONModel

3.YYModel

YYModel

(1).相比上面不用继承

YYJSONModel.h

@class YYJSONDataModel,YYJSONFeedModel;

@interface YYJSONModel : NSObject
@property (nonatomic,assign)int status;
@property (nonatomic,copy)NSString *resTime;
@property (nonatomic,copy)NSString *uni;
@property (nonatomic,copy)NSString *localUni;
@property (nonatomic,strong)YYJSONDataModel *data;
@end

@interface YYJSONDataModel : NSObject
@property (nonatomic,assign)NSInteger isIntro;
@property (nonatomic,copy)NSString *feedDownType;
@property (nonatomic,assign)NSInteger feedLastIndex;
@property (nonatomic,strong)NSArray *feed;
@property (nonatomic,copy)NSString *downText;
@property (nonatomic,copy)NSString *lastTimestamp;
@end

@interface YYJSONFeedModel : NSObject
@property (nonatomic,copy)NSString *newsId;
@property (nonatomic,copy)NSString *longTitle;
@property (nonatomic,copy)NSString *title;
@property (nonatomic,copy)NSString *source;
@property (nonatomic,copy)NSString *link;
@property (nonatomic,copy)NSString *pic;
@property (nonatomic,copy)NSString *kpic;
@property (nonatomic,copy)NSString *des;
@property (nonatomic,copy)NSString *pubDate;
@property (nonatomic,assign)NSInteger interestSwitch;
@end

YYJSONModel.m

@implementation YYJSONModel

@end

@implementation YYJSONDataModel
// 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。
+ (NSDictionary *)modelContainerPropertyGenericClass {
    return @{@"feed" : @"YYJSONFeedModel" };
    /*
     **@"feed" : [YYJSONFeedModel class],
     @"feed" : YYJSONFeedModel.class,
     @"feed" : @"YYJSONFeedModel"
     */
}

@end

@implementation YYJSONFeedModel
+ (NSDictionary *)modelCustomPropertyMapper {
    return @{@"des":@"intro"};
}
@end

(2).简单说明

json转模型

方法: + (instancetype)yy_modelWithJSON:(id)json;

模型转字符串

方法: - (NSString *)yy_modelToJSONString

字典转模型

方法: + (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary ;

声明数组、字典或者集合里的元素类型时要重写

方法: + (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;

字典里的key值与模型的属性值不一致要重复

方法: + (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;

下面两者是属性值在两个dic与模型之间的转化方法

方法: - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic ;

方法: - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;

你可以把一个或一组 json key (key path) 映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。

在 json->model 的过程中:如果一个属性对应了多个 json key,那么转换过程会按顺序查找,并使用第一个不为空的值。

在 model->json 的过程中:如果一个属性对应了多个 json key (key path),那么转换过程仅会处理第一个 json key (key path);如果多个属性对应了同一个 json key,则转换过过程会使用其中任意一个不为空的值。

被包含的Model什么都不用做,如果属性名不对应,也要实现映射的方法
“+ (NSDictionary *)modelCustomPropertyMapper”

关键值替换

+ (NSDictionary *)modelCustomPropertyMapper {
    return @{@"des":@"intro"};
}

黑名单 与 白名单:

 @interface User
 @property NSString *name;
 @property NSUInteger age;
 @end
 
 @implementation Attributes
 // 如果实现了该方法,则处理过程中会忽略该列表内的所有属性
 + (NSArray *)modelPropertyBlacklist {
 return @[@"test1", @"test2"];
 }
 // 如果实现了该方法,则处理过程中不会处理该列表外的属性。
 + (NSArray *)modelPropertyWhitelist {
 return @[@"name"];
 }

数据校验与自定义转换

JSON:

{
"name":"Wang",
"timestamp" : 1544564567
}

Model:

 @interface User
 @property NSString *name;
 @property NSDate *putDate;
 @end
 @implementation User
 
 // 当 JSON 转为 Model 完成后,该方法会被调用。
 // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。
 // 你也可以在这里做一些自动转换不能完成的工作。
 - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
 NSNumber *timestamp = dic[@"timestamp"];
 if (![timestamp isKindOfClass:[NSNumber class]]) return NO;
 _putDate = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue];
 return YES;
 }
 
 // 当 Model 转为 JSON 完成后,该方法会被调用。
 // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。
 // 你也可以在这里做一些自动转换不能完成的工作。
 - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
 if (! _putDate) return NO;
 dic[@"timestamp"] = @(n.timeIntervalSince1970);
 return YES;
 }

容器类属性

@interface YYJSONDataModel : NSObject
@property (nonatomic,assign)NSInteger isIntro;
@property (nonatomic,copy)NSString *feedDownType;
@property (nonatomic,assign)NSInteger feedLastIndex;
@property (nonatomic,strong)NSArray *feed;
@property (nonatomic,copy)NSString *downText;
@property (nonatomic,copy)NSString *lastTimestamp;
@end
// 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。
 + (NSDictionary *)modelContainerPropertyGenericClass {
 return @{@"feed" : [YYJSONFeedModel class]};
 }

其他等等可以自行查看YYModel

4.MJExtension

MJExtension

(1).MJExtension是一套字典和模型之间互相转换的超轻量级框架

MJExtensionModel.h

@class MJExtensionDataModel,MJExtensionFeedModel;

@interface MJExtensionModel : NSObject
@property (nonatomic,assign)int status;
@property (nonatomic,copy)NSString *resTime;
@property (nonatomic,copy)NSString *uni;
@property (nonatomic,copy)NSString *localUni;
@property (nonatomic,strong)MJExtensionDataModel *data;
@end

@interface MJExtensionDataModel : NSObject
@property (nonatomic,assign)NSInteger isIntro;
@property (nonatomic,copy)NSString *feedDownType;
@property (nonatomic,assign)NSInteger feedLastIndex;
@property (nonatomic,strong)NSArray *feed;
@property (nonatomic,copy)NSString *downText;
@property (nonatomic,copy)NSString *lastTimestamp;
@end

@interface MJExtensionFeedModel : NSObject
@property (nonatomic,copy)NSString *newsId;
@property (nonatomic,copy)NSString *longTitle;
@property (nonatomic,copy)NSString *title;
@property (nonatomic,copy)NSString *source;
@property (nonatomic,copy)NSString *link;
@property (nonatomic,copy)NSString *pic;
@property (nonatomic,copy)NSString *kpic;
//@property (nonatomic,copy)NSString *intro;
@property (nonatomic,copy)NSString *des;
@property (nonatomic,copy)NSString *pubDate;
@property (nonatomic,assign)NSInteger interestSwitch;
@end

MJExtensionModel.m

@implementation MJExtensionModel

@end

@implementation MJExtensionDataModel
+ (NSDictionary *)mj_objectClassInArray{
    return @{
             @"feed" : @"MJExtensionFeedModel",
             };
}

@end

@implementation MJExtensionFeedModel

+ (NSDictionary *)mj_replacedKeyFromPropertyName {
    return @{@"des":@"intro"};
}

@end

(2).简单说明

MJExtension是一套字典和模型之间互相转换的超轻量级框架

只需要一行代码,就能实现模型的所有属性进行Coding(归档和解档)

JSON --> Model、Core Data Model

JSONString --> Model、Core Data Model

Model、Core Data Model --> JSON

JSON Array --> Model Array、Core Data Model Array

JSONString --> Model Array、Core Data Model Array

Model Array、Core Data Model Array --> JSON Array

还是

其他等等可以自行查看MJExtension

iOS JSON 模型转换库评测

终于结束了。

最后凑个图.png
上一篇下一篇

猜你喜欢

热点阅读