码迷,mamicode.com
首页 > Web开发 > 详细

让Json和Model相处的融洽点

时间:2015-07-16 16:33:33      阅读:142      评论:0      收藏:0      [点我收藏+]

标签:ios   json   orm   

iOS里服务器返回来的JsonData通过自带的json解析类或者什么JsonKit,SBJson一类的框架都可以方便的完成解析工作,如果不觉得麻烦的话从这里开始通过KVC取值就完全可以搞定了~可对字符串这种东西进行硬编码...

所以转成Model对象还是比较必要的~而json对象到model对象的映射也有很多东西可以做了~诸如JSONModel,Mantle什么的~最值得一提的是传智播客的当家花旦李明杰老师所写的MJExtension~非常出色,尤其在转化效率上~不敢说今天拿出来的这东西比MJExtension好,只能说更符合笔者自己简单粗暴有效的开发和使用习惯~

经过测试,用我的Macbook对后文给出的1000个相同的测试词典所组成的数组进行映射操作,MJExtension耗时仅0.07秒,而我写的这套工具则花了0.25秒左右...耗时在3~4倍的区间~由于是通过对于模型名称和类的名称进行判断和字符串处理整合来进行映射,更高的耗时也是自然的~通过GCD的并发操作进行处理也并没有太明显的效率提升~

考虑后得到的结论是对每一个模型进行转化的时候都需要对齐内部结构进行完整扫描,对于单个模型的转化这是自然,但是对于数组的整体映射,由于数组保存的模型是相同的,那么长度1000的数组有999次扫描都是没必要的~1:1的线性耗时增加造成了整体的效率降低~作为第一版就先这样吧,毕竟即使没有通过缓存类结构来提升效率,1000个模型0.25秒的映射速度目前来看也还是可以接受的~

接下来说说这究竟是个什么东西

先看一组用来做测试的假数据


-(NSDictionary *)fakeData{
    return
@{
@"name":@"jack",@"id":@"123",@"code":@"4567890-",
@"other":@{@"attr1":@"value1",@"attr2":@"150",@"attr3":@"0.8"},
@"test":@"3",
@"d":@"4.5",
@"l":@3456789,
@"f":@"123.123",
@"b":@"1",
@"date1":@"13567890456",
@"date2":@"2015-4-3 11:18:24",
@"arr":@[
    @{@"attr1":@"value1",@"attr2":@"150",@"attr3":@"0.8"},
    @{@"attr1":@"value1",@"attr2":@"150",@"attr3":@"0.8"}],
@"path":@{
    @"third":@{@"name":@"rose",@"age":@"123"},
    @"3rd":@{@"name":@"rose",@"age":@"123"},
    @"sub":@{
        @"fourth":@{@"name":@"rose",@"age":@"123"},
        @"4th":@{@"name":@"rose",@"age":@"123"}}}
};
}



数据量并不大,可以看到最外层的数据包含了字符串,日期,数字,数组,词典这些常用的数据类型~KEY:`l`对应的@3456789映射到long类型的数据,`f`映射到float类型的数据,虽然目标是数字类型,但是json中究竟是什么格式并没有关系~

而日期方面,可以看出date1使用的是时间戳,而date2使用的是日期字符串~针对于日期数据的处理全部通过NESDateHandler来完成,坦白说这个东西已经可以分出来作为一个独立的日期工具存在了~写这东西的时候多少还是走了点心,不过这倒不是今天的重点~贴出这个类的测试用例就差不多包含了它的全部用法了~完整的测试用例和源码在最后都可以下载


-(void)testBlockHandler
{
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"yyyy-MM-dd";
    NSDate *date1 = [NESDateHandler dateWith:@"date:2015-10-10" using:^NSDate *(id object) {
        NSString *dateString = [[object componentsSeparatedByString:@":"] lastObject];
        return [fmt dateFromString:dateString];
    }];
    NSDate *date2 = [fmt dateFromString:@"2015-10-10"];
    XCTAssertEqualObjects(date1, date2);
}
-(void)testCustomFormatter
{
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"yyyy-MM-dd";
    [NESDateHandler setDateFormatter:fmt]; 
    NSDate *date1 = [NESDateHandler dateWith:@"2015-10-10 11:11:11"];
    NSDate *date2 = [NESDateHandler dateWith:@"2015"]; 
    //日期字符串不匹配,按照时间戳处理,两者得到的结果应该相同
    XCTAssertEqualObjects(date1, date2); 
    date1 = [NESDateHandler dateWith:@"2015-10-10"];
    date2 = [fmt dateFromString:@"2015-10-10"]; 
    XCTAssertEqualObjects(date1, date2); 
    [NESDateHandler setDateFormatter:[NESDateHandler defaultDateFormatter]];
}
-(void)testDefaultDateStringHandler
{
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:60*60*24*365*45 + 60*60*24 * 11];
    //45年+11个闰年+8个时区
    NSString *str = @"2015-01-01 08:00:00";
    NSString *target = [NESDateHandler stringWith:date];
    XCTAssertEqualObjects(str, target);
}
-(void)testCustomDateStringHandler
{
    NSString *str = @"自定义日期处理器"; 
    [NESDateHandler setDateStringHandler:^NSString *(NSDate * date) {
        return @"自定义日期处理器";
    }]; 
    NSString *target = [NESDateHandler stringWith:[NSDate date]]; 
    XCTAssertEqualObjects(str, target); 
    [NESDateHandler setDateStringHandler:[NESDateHandler defaultDateStringHandler]]; 
}
-(void)testCustomDateHandler
{
    [NESDateHandler setDateHandler:^NSDate *(id object) {
        return [NSDate dateWithTimeIntervalSince1970:60*60*24*365*100];
    }]; 
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:60*60*24*365*100];
    NSDate *target = [NESDateHandler dateWith:@"2015"]; 
    XCTAssertNotNil(target);
    XCTAssertEqualObjects(date, target); 
    [NESDateHandler setDateHandler:[NESDateHandler defaultDateHandler]]; 
}
-(void)testDateFromInvalidateString
{
    NSDate *date = [NESDateHandler dateWith:@"a876yikdvhas"];
    NSDate *dateNow = [NSDate date];
    XCTAssertNotNil(date);
    NSTimeInterval t = dateNow.timeIntervalSince1970 - date.timeIntervalSince1970;
    XCTAssert(t > 0 && t < 0.001);
}
-(void)testDateFromTimeInterval
{
    NSDate *date1 = [NESDateHandler dateWith:@"2015/4/5 22:11:11.123"];
    NSDate *date2 = [NESDateHandler dateWith:@"2015"];
    XCTAssertNotNil(date1);
    XCTAssertNotNil(date2);
    XCTAssertEqualObjects(date1, date2);
}
-(void)testDateFromValidateString
{
    NSDate *date1 = [NESDateHandler dateWith:@"2014-4-5 22:10:19"];
    XCTAssertNotNil(date1);
}

关于日期字符串的处理倒是MJExtension没有的功能,也是我比较需要的一个东西~接下来就是针对上面的假数据应该如何定义模型了

#import <Foundation/Foundation.h>
#import "NESOtherModel.h"
#import "NESThirdModel.h"
#import "NESFourthModel.h"
@interface NESDemoModel : NSObject
@property (nonatomic,retain) NSString *name;
@property (nonatomic,retain) NSString *test_id;
@property (nonatomic,retain) NSString *code;
@property (nonatomic,retain) NESOtherModel *other;
@property (nonatomic,assign) int test;
@property (nonatomic,assign) double d;
@property (nonatomic,assign) long l;
@property (nonatomic,assign) float f;
@property (nonatomic,assign) BOOL b;
@property (nonatomic,retain) NSDate *date1;
@property (nonatomic,retain) NSDate *date2;
@property (nonatomic,retain) NSArray *other_arr;
@property (nonatomic,retain) NESThirdModel *_path_third;
@property (nonatomic,retain) NESThirdModel *_path_3rd;
@property (nonatomic,retain) NESFourthModel *_path_sub_fourth;
@property (nonatomic,retain) NESFourthModel *_path_sub_4th;
@property (nonatomic,assign) NSString * notExists_;
@end

没有任何协议,定义时不需要引入额外的头文件,直接继承自NSObject就好~属性的命名规则大体如下:

1.直接映射,属性名与json键值相同
2.间接映射,以字母开头,以`_`连接,前边为任意字符串,后边为json键,
例:@property (nonatomic,retain) NSString *test_id;对应json中的id
3.数组映射,对于数组类型的属性,用`_`连接模型名称和json键即可完成映射
例:@property (nonatomic,retain) NSArray *other_arr;对应json中的arr,至于用什么模型进行封装后文会说明
4.路径映射,当希望将json内部的某个对象直接映射成为模型属性的时候使用,以`_`开头,每一级路径以`_`连接
以json键作为结尾,路径映射可以包含多级路径
例:@property (nonatomic,retain) NESFourthModel *_path_sub_fourth;这里是二级路径映射,对照上边的数据源很容易理解
5.忽略属性,当需要在模型中定义一个json对象中没有的属性,同时在进行json字符串转化时将其忽略的属性时,直接在属性名最后加`_`
例:@property (nonatomic,assign) NSString * notExists_;这个就是在映射时不做任何操作的字段了

关于json对象内数组的映射,用到了一个叫做NESModelFormat的类,其中定义了全局的前缀和后缀,默认分别是空字符串和"Model",从头文件定义中看到引入了一个叫做`NESOtherModel`的类~这里`NES`就是前缀,需要在NESModelFormat中对齐进行手都配置,当然配置是全局的,只要在适当的位置配置一次就可以了~而这样`other_arr`的效果就是把json中arr对应的数组映射成为NESOtherModel类的对象组成的数组~实际使用的时候采用什么前缀什么后缀自己配置就是了~

映射完成后的对象反向生成的json字符串就是

{"b":"1","test":"3","f":"123.123","name":"jack","id":"123","code":"4567890-","other":{"attr3":"0.8","attr1":"value1","attr2":"150"},"d":"4.5","l":"3456789","date1":"2399-12-14 02:27:36","date2":"2015-04-03 11:18:24","arr":[{"attr3":"0.8","attr1":"value1","attr2":"150"},{"attr3":"0.8","attr1":"value1","attr2":"150"}],"path":{"third":{"age":"123","name":"rose"},"3rd":{"age":"123","name":"rose"},"sub":{"fourth":{"age":"123","name":"rose"},"4th":{"age":"123","name":"rose"}}}}

考虑到篇幅,就不加格式了~

而模型对象转词典的时候用了比较偷懒的写法...

 [NSJSONSerialization JSONObjectWithData:[self.jsonString dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];

到这里所有的功能也就出来了~所有的API定义在"NSObject+NESModel.h"中~也就三个方法

+(id)mappingWithObject:(id)object;
-(NSString *)jsonString;
-(id)foundationModel;

用起来大概是这个样子:

NESDemoModel *model = [NESDemoModel mappingWithObject:self.fakeData];
NSArray *arr = [NESDemoModel mappingWithObject:@[self.fakeData,self.fakeData,self.fakeData,self.fakeData]];
NSDictionary *dict = arr.foundationModel;

ok~大体就是这些内容了~还有考虑欠妥和功能不够完善的地方~欢迎大家提提意见,或者有什么尚未满足的需求需要添加也可以联系我~能力范围内的会尽快添加进来~
当然,这个毕竟是我个人比较喜欢的方式,简单总结一下,能够正确处理NSDate类型的属性,不需要在实现文件中写代码,属性明见名知意~如果你也喜欢这种方式欢迎使用这套工具~
如果对运行效率方面要求更高,那么还是强烈推荐使用MJExtension~

最后附上下载路径:
https://github.com/DominicNestor/NESModelExtDemo

版权声明:本文为博主原创文章,未经博主允许不得转载。

让Json和Model相处的融洽点

标签:ios   json   orm   

原文地址:http://blog.csdn.net/nestalk/article/details/46913345

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!