BAModel的使用

我们知道数据在网络传递的过程中是以数据流的方式传递,而我们在app中真正拿到的数据是NSData,所以有时候我们需要把这些数据解析成NSArray或者NSDictionary甚至我们需要的Model对象。我们之前说过通过设置服务器的Content-Type就可以把我们从Responese拿到的数据是NSArray或者NSDictionary,那底层又是怎么做的呢?以及我们怎么样把一个NSDictionary转成一个我们需要的model对象呢?

我们来看看在Response目录下的BAResponseSerializer类,具体代码如下:

从代码中我们可以看出,其实在BAResponseSerializer类中对返回的数据包头进行分析了,如果它的Content-Type类型为application/json时,我们会调用系统的JSON解析类(注意Content-Type的大小写),相应的你可以根据相应的需求修改相应的条件。

- (id)responseObjectForURLResponse:(NSURLResponse *)response data:(NSData *)data {
if (data == nil || ![response isKindOfClass:[NSHTTPURLResponse class]]) return nil;

id object = nil;
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;
if ([HTTPResponse.allHeaderFields[@"Content-Type"] ba_containsString:@"application/json"]) {
NSError *error = nil;
object = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (error) {
object = data;
}
} else {
object = data;
}

return object;
}

现在我们知道为什么必须要服务器返回的Content-Type设置为application/json了,那如果我们需要把NSDictionary转成Model呢?

简单Json转化

举个例子,假如服务器返回的数据如下,我们需要转成一个model对象应该怎么做呢?

{
"user_id" : 1001,
"user_name" : "BeyondAbel",
"sex" : "男"
}

首先它需要继承BAModel类,然后实现+ (NSDictionary *)dictionaryKeyPathsForPropertyNames方法。我们来看一个真实代码:

创建一个BAUserModel类代码如下

@interface BAUserModel : BAModel

@property (nonatomic, assign, readonly) NSInteger userID;
@property (nonatomic, strong, readonly) NSString *userName;
@property (nonatomic, strong, readonly) NSString *sex;

@end

@implementation BAUserModel

+ (NSDictionary *)dictionaryKeyPathsForPropertyNames {
retur @{
@"userID" : @"user_id",
@"userName" : @"user_name",
@"sex" : @"sex",
};
}

@end

从上面的代码可以看出,我们要实现的代码非常少,就可以把一个NSDictionary转成一个Model对象了,其中dictionaryKeyPathsForPropertyNames中,所有的key值为BAUserModel类的属性名称,value值为服务器返回的json串的key值。这个时候我们只需要把创建一个BAUserModel实例初始化数据就可以了

BAUserModel *userModel = [[BAUserModel alloc] initWithDictionary:result.body];

数组Model转化

到现在为止我们都知道了基本的NSDictionary转Model的方式了,那么服务器给我们的是一组数据呢?比如下面的数据呢?

[
{
"user_id" : 1001,
"user_name" : "BeyondAbel",
"sex" : "男"
},
{
"user_id" : 1002,
"user_name" : "may",
"sex" : "女"
},
{
"user_id" : 1003,
"user_name" : "tammy",
"sex" : "女"
},
]

其实也是非常简单,我们可以去定义一个数组来保存解析出来的BAUserModel实例,然后遍历服务器返回的数组并解析数组中的字典成对象,最后一个一个添加接收数组。具体如下:

NSMutableArray *userModels = [[NSMutableArray alloc] initWithCapacity:0];
for (NSDictionary *userDictionary in result.body) {
BAUserModel *userModel = [[BAUserModel alloc] initWithDictionary:userDictionary];
[userModels addObject:userModel];
}

当然上面的方法是大家都能想得到的一个方法,下面我介绍一个技巧性的方法,首先你需要导入NSArray+BAAdditions.h头文件。

NSArray *userModels = [result.body ba_mappedArrayWithBlock:^id(id obj) {
return [[BAUserModel alloc] initWithDictionary:obj];
}];

上面的二种方式都可以实现相应的要求,个人推荐第二种方法。

嵌套Model转化

上面讲解了简单的数据转成Model的使用方法,但如果我们遇到实例中的属性也是一个对象呢?那又该怎么办呢?

{
"user_id" : 1001,
"user_name" : "BeyondAbel",
"sex" : "男",
"app" :
{
"app_id" : 3,
"app_name" : "金蛋理财",
"link" : "https://www.jindanlicai.com"
}
}

首先我们按照之前的方式创建二个对象BAUserModel和BAAppModel

@interface BAUserModel : BAModel

@property (nonatomic, assign, readonly) NSInteger userID;
@property (nonatomic, strong, readonly) NSString *userName;
@property (nonatomic, strong, readonly) NSString *sex;
@property (nonatomic, strong, readonly) BAAppModel *appModel;

@end

+ (NSDictionary *)dictionaryKeyPathsForPropertyNames {
return @{
@"userID" : @"user_id",
@"userName" : @"user_name",
@"sex" : @"sex",
@"appModel" : @"app",
};
}

@interface BAAppModel : BAModel

@property (nonatomic, assign, readonly) NSInteger appID;
@property (nonatomic, strong, readonly) NSString *appName;
@property (nonatomic, strong, readonly) NSString *link;

@end

@implementation BAAppModel

+ (NSDictionary *)dictionaryKeyPathsForPropertyNames {
return @{
@"appID" : @"app_id",
@"appName" : @"app_name",
@"link" : @"link",
};
}

@end

然后我们对返回的数据进行解析

BAUserModel *userModel = [[BAUserModel alloc] initWithDictionary:result];

我们对userModel断点时发现,userModel中的appModel的数据类型竟然是NSDictionay类型,而不是我们想的BAAppModel对象。

这是怎么回事呢?原来我们并没有对appMoel那部分数据做转换,而是直接把NSDictionay给appModel保存了,那如果我们要对其数据转换成BAAppModel又应该怎么做呢?

我们只需要在BAUserModel导入NSValueTransformer+BATransformers.h文件,并且在BAUserModel类中实现转换方法appModelValueTransformer:。如下所示

#import "NSValueTransformer+BATransformers.h"

@implementation BAUserModel

+ (NSDictionary *)dictionaryKeyPathsForPropertyNames {
return @{
@"userID" : @"user_id",
@"userName" : @"user_name",
@"sex" : @"sex",
@"appModel" : @"app",
};
}

+ (NSValueTransformer *)appModelValueTransformer {
return [NSValueTransformer ba_transformerWithModelClass:[BAAppModel class]];
}

这个时候我们再来看一下appModel的数据类型,发现这个时候是BAAppModel数据类型了。

为什么这样写就可以了呢?如果之后遇到相同的问题又应该怎么办呢?其实只要涉及到类型转换,其中appModelValueTransformer中的appModel是需要转换的属性名称,ba_transformerWithModelClass:后面的Class是用这个类来转换。