【疯狂造轮子-iOS】JSON转Model系列之二
本文转载请注明出处 —— polobymulberry-博客园,2.1 没有考虑JSON数据并不一定是NSDictionary类型,上一篇《【疯狂造轮子-iOS】JSON转Model系列之一》实现了一个简陋的JSON转Model的库,不过还存在很多问题。下面我会尝试一个个去解决。,有时候JSON并不一定是NSDictionary类型,可能是一个字符串,也可能是NSData类型的数据。不过不管是哪种类型,统统先将其转化为NSData数据,然后使用+[NSJSONSerialization JSONObjectWithData:options:error:]来转化。所以我在initWithAttributes:上面又封装了一层。,2.2 没有考虑用户传入的JSON数据的key值和property的名称不一致,为此,我在ViewController添加了两个sample。分别用来解析NSString类型的JSON数据和NSData类型的JSON数据。,输出结果也是正确的:,,我第一反应是使用一个映射表。也就是说用户使用时需要自定义一套property和key的映射表。YYModel中使用了一个+ (NSDictionary *)modelCustomPropertyMapper函数,用户可以自定义该函数达到映射表的效果,而这个函数是放在一个protocol中的。我挺认同这种设计的,因为modelCustomPropertyMapper这种函数和Model是一种组合关系,可有可无(optional),所以设计成协议更合适。但是作者在设计protocol又说了一句:,什么意思呢,就是说你自定义一个NSObject子类(如YYBook)时,如果想实现自定义的property映射关系,只需要实现modelCustomPropertyMapper函数即可,而不需要写成@interface YYBook : NSObject <YYModel>。作者的意思是你遵不遵循YYModel这个protocol都没事,反正你只要在YYBook实现了modelCustomPropertyMapper即可。具体解释,大家请参考这个issue。,2.3 没有考虑JSON数据的value值不一定是NSString类型,这种设计我不是很赞同,我是有洁癖的人,要不然你就别定义YYModel这个protocol,说明文档里面着重说明一下就行。所以此处我还是选择判断NSObject的子类是否遵循protocol,也就是说只有遵循了这个protocol,才能自定义property映射关系。,首先我们看如何使用自定义propertyMapper。我先建立一个PJXUserPropertyMapper类,遵循了JSONProtocol协议,并实现了propertyMapper协议函数。,- (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;
},// 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);
},随后我定义了一个example。,是不是感觉调用上和之前的非property映射没什么区别?那是因为我们需要在initWithJSONData中增加一些东西。,// There’s no need to add ‘<YYModel>’ to your class header.
@protocol YYModel <NSObject>,具体的做法是在PropertyWithDictionary函数增加了一个查表操作。,// 遵循JSONProtocol协议,这个JSONProtocol中定义的就是我的propertyMapper协议函数
@interface PJXUserPropertyMapper : NSObject <JSONProtocol>
@property (nonatomic, copy) NSString* username; // 用户名
@property (nonatomic, copy) NSString* password; // 密码
@property (nonatomic, copy) NSString* avatarImageURL; // 头像的URL地址
@end
@implementation PJXUserPropertyMapper
// 实现propertyMapper这个协议方法
+ (NSDictionary *)propertyMapper
{
return @{@”Username” : @”username”,
@”Password” : @”password”,
@”AvatarImageURL” : @”avatarImageURL”};
}
@end,这样就可以啦.我们看看效果:,,开始的时候,挺担心我这种写法会不会不兼容别的数据类型。不过我觉得应该没什么问题,毕竟我使用的setter方法本质上没啥问题,我的类型全用id来代替了(事实上,我的想法大错特错):,不过本着不怕一万,就怕万一的心态。我还是做了一个example来试验一下:,你猜输出啥?,
,其他都正确,唯独我们的blogIndex出错了。这里确实是我欠考虑了,类似NSInteger,BOOL这些NSNumber类型(我暂时只考虑这些常用类型)需要单独处理一下。这一部分看起来容易,但是为了处理这种特殊情况确实要下很大功夫。比如你得先判断该属性是不是double或int这种类型,只有判断除了该属性是double还是int,你才能正确使用setter方法,而此处的调用方式也要单独写一个,因为和之前调用方式有一些些区别,需要判断Number的类型是double,是int,还是BOOl…….,对此我在PJXPropertyInfo中定义了两个函数,一个叫isNumber,用来判断该属性是不是一个Number,另一个叫setNumberValue:withModelSelf:,用来给是Number类型的属性赋值。另外,我仿照YYModel(比YYModel简化很多了)建了一个PJXEncodingType的enum类型,用来存储Number的类型(int?double?BOOL?……),与之配套的还有一个PJXGetEncodingType函数,来获取当前属性的类型(是int?double?BOOL?),具体怎么做还挺复杂的,后面会详细说明。,#pragma mark – PropertyMapper Sample
– (void)runPropertyMapperSample
{
NSDictionary *userDict = @{@”Username” : @”shuaige”,
@”Password” : @””,
@”AvatarImageURL” : @”http://www.example.com/shuaige.png”};
PJXUserPropertyMapper *user = [[PJXUserPropertyMapper alloc] initWithJSONData:userDict];
NSLog(@”runPropertyMapperSample\n”);
NSLog(@”—————————————-“);
NSLog(@”username:%@\n”,user.username);
NSLog(@”password:%@\n”,user.password);
NSLog(@”avatarImageURL:%@\n”,user.avatarImageURL);
},代码如下:,// 注意我传入的dictionary就是用户提供的JSON数据
// 比如此处传入的key==@”username”,value==@”shuaige”
static void PropertyWithDictionaryFunction(const void *key, const void *value, void *context)
{
NSString *keyStr = (__bridge NSString *)(key);
……
// 如果使用了JSONProtocol,并且自定义了propertyMapper,那么还需要将keyStr转化下
if ([modelSelf conformsToProtocol:@protocol(JSONProtocol)] && [[modelSelf class] respondsToSelector:@selector(propertyMapper)]) {
keyStr = [[[modelSelf class] propertyMapper] objectForKey:keyStr];
}
……
},有了上述的几个方法,后面就好办了,只需在PropertyWithDictionaryFunction函数中添加一个Number的判断就行:,这下终于成功了:,((void (*)(id, SEL, id))(void *) objc_msgSend)(modelSelf, info.setter, setValue);,,@interface PJXUserVariousType : NSObject
@property (nonatomic, copy) NSString *blogTitle; // 博客标题
@property (nonatomic, strong) NSURL *blogURL; // 博客网址
@property (nonatomic, assign) NSInteger blogIndex; // 博客索引值
@property (nonatomic, strong) NSDate *postDate; // 博客发布时间
@property (nonatomic, strong) NSArray *friends; // 我的好友名称
@property (nonatomic, strong) NSSet *collections; // 我的收藏
@end
@implementation PJXUserVariousType
@end
#pragma mark – VariousType Sample
– (void)runVariousTypeSample
{
NSDictionary *userDict = @{@”blogTitle” : @”iOS developer”,
@”blogURL” : @”http://www.example.com/blog.html”,
@”blogIndex” : @,
@”postDate” : [NSDate date],
@”friends” : @[@”meinv1″, @”meinv2″, @”meinv3″],
@”collections” : @[@”shuaige1″, @”shuaige2″, @”shuaige3″]};
PJXUserVariousType *user = [[PJXUserVariousType alloc] initWithJSONData:userDict];
NSLog(@”runVariousTypeSample\n”);
NSLog(@”—————————————-“);
NSLog(@”blogTitle:%@\n”,user.blogTitle);
NSLog(@”blogURL:%@\n”,user.blogURL);
NSLog(@”blogIndex:%ld\n”,user.blogIndex);
NSLog(@”postDate:%@\n”,user.postDate);
NSLog(@”friends:%@\n”,user.friends);
NSLog(@”collections:%@\n”,user.collections);
}
2. 本站不保证所提供所有下载的资源的准确性、安全性和完整性,资源仅供下载学习之用!如有链接无法下载、失效或广告,请联系客服处理,有奖励!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或教程,您可以投稿发布,成功分享后有RB奖励和额外RMB收入!
磊宇堂正在使用的服务器 维护管理由磊宇云服务器提供支持
磊宇堂 » 【疯狂造轮子-iOS】JSON转Model系列之二