1. 前言
上一篇《【疯狂造轮子-iOS】JSON转Model系列之一》实现了一个简陋的JSON转Model的库,不过还存在很多问题。下面我会尝试一个个去解决。
2. 存在问题及解决思路
2.1 没有考虑JSON数据并不一定是NSDictionary类型
有时候JSON并不一定是NSDictionary类型,可能是一个字符串,也可能是NSData类型的数据。不过不管是哪种类型,统统先将其转化为NSData数据,然后使用+[NSJSONSerialization JSONObjectWithData:options:error:]来转化。所以我在initWithAttributes:上面又封装了一层。
- (instancetype)initWithJSONData:(id)json
{
NSDictionary *dict = [self pjx_dictionaryWithJSON:json];
return [self initWithAttributes:dict];
}
/**
* @brief 将NSString和NSData格式的json数据转化为NSDictionary类型
*/
- (NSDictionary *)pjx_dictionaryWithJSON:(id)json
{
if (!json) {
return nil;
}
// 若是NSDictionary类型,直接返回
if ([json isKindOfClass:[NSDictionary class]]) {
return json;
}
NSDictionary *dict = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSString class]]) {
// 如果是NSString,就先转化为NSData
jsonData = [(NSString*)json dataUsingEncoding:NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData && [jsonData isKindOfClass:[NSData class]]) {
// 如果时NSData类型,使用NSJSONSerialization
NSError *error = nil;
dict = [NSJSONSerialization JSONObjectWithData:jsonData options: error:&error];
if (error) {
NSLog(@"pjx_dictionaryWithJSON error:%@", error);
return nil;
}
if (![dict isKindOfClass:[NSDictionary class]]) {
return nil;
}
}
return dict;
}
为此,我在ViewController添加了两个sample。分别用来解析NSString类型的JSON数据和NSData类型的JSON数据。
// NSString类型的JSON数据
- (void)runSimpleSample2
{
NSString *userStr = @" \
{ \
\"username\" : \"shuaige\", \
\"password\" : \"123456\", \
\"avatarImageURL\" : \"http://www.example.com/shuaige.png\" \
}";
PJXUser *user = [[PJXUser alloc] initWithJSONData:userStr];
NSLog(@"runSimpleSample2\n");
NSLog(@"----------------------------------------");
NSLog(@"username:%@\n",user.username);
NSLog(@"password:%@\n",user.password);
NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
}
// NSData类型的JSON数据
- (void)runSimpleSample3
{
NSString *userInfoFilePath = [[NSBundle mainBundle] pathForResource:@"UserInfo" ofType:@"txt"];
NSData *data = [NSData dataWithContentsOfFile:userInfoFilePath];
PJXUser *user = [[PJXUser alloc] initWithJSONData:data];
NSLog(@"runSimpleSample3\n");
NSLog(@"----------------------------------------");
NSLog(@"username:%@\n",user.username);
NSLog(@"password:%@\n",user.password);
NSLog(@"avatarImageURL:%@\n",user.avatarImageURL);
}
输出结果也是正确的:
2.2 没有考虑用户传入的JSON数据的key值和property的名称不一致
我第一反应是使用一个映射表。也就是说用户使用时需要自定义一套property和key的映射表。YYModel中使用了一个+ (NSDictionary *)modelCustomPropertyMapper函数,用户可以自定义该函数达到映射表的效果,而这个函数是放在一个protocol中的。我挺认同这种设计的,因为modelCustomPropertyMapper这种函数和Model是一种组合关系,可有可无(optional),所以设计成协议更合适。但是作者在设计protocol又说了一句:
// There's no need to add '<YYModel>' to your class header.
@protocol YYModel <NSObject>
什么意思呢,就是说你自定义一个NSObject子类(如YYBook)时,如果想实现自定义的property映射关系,只需要实现modelCustomPropertyMapper函数即可,而不需要写成@interface YYBook : NSObject <YYModel>。作者的意思是你遵不遵循YYModel这个protocol都没事,反正你只要在YYBook实现了modelCustomP