在 Podfile 文件里添加:
target 'TargetName' do
pod 'SCJSONUtil'
end
《一看二建三解析》,直接看代码比较直观:
{
"code": "0",
"content": {
"gallery": [
{
"isFlagship": "0",
"name": "白色情人节 与浪漫牵手",
"pic": "http://pic16.shangpin.com/e/s/15/03/06/20150306174649601525-10-10.jpg",
"refContent": "http://m.shangpin.com/meet/189",
"type": "5"
},
...
]
}
}
@interface GalleryModel : NSObject
@property (nonatomic, copy) NSString *isFlagship;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *pic;
@property (nonatomic, copy) NSString *refContent;
@property (nonatomic, copy) NSString *type;
@end
假设responseJSON是服务器返回的json数据,常规的写法是先判断 code 是否等于 0,然后再取出 gallery 对应的json 转成 model; 然而 SCJSONUtil
提供了更加便捷的方法完成这些步骤:
//1.根据 keypath 取出目标json
id findedJSON = SCFindJSONwithKeyPath(@"content/gallery", responseJSON);
//2.使用 JSONUtil 解析
NSArray *models = SCJSON2Model(findedJSON, @"GalleryModel");
//models 就是你想要的GalleryModel数组了!
其他的用法,可以参考demo。
这里要解释下第二点,因为这里我和其他的 JSON 转 Model 框架的实现大不相同!先来看下其他的主流思想:
知道了主流做法后,再来看下 JSONUtil 是如何处理的吧,你会发现 JSONUtil 有很有特色,因为随大流地的去模仿做 cache ,遍历 model 的全部属性,之所以没有这么做,是因为我觉得 没有必要!
做 cache 是为了下次解析更快,你知道将一个json转换出一个model需要多久吗?对于普通JSON其实不用担心的,如果真的担心,何不使用 GCD 去异步线程里做呢?试问你愿意多看1秒钟(异步GCD解析)的loading还是愿意让App卡顿0.5秒钟(主线程解析)?
举个不太恰当的例子哈:如果model是个3G开关,该开关只会在App启动时请求一次,那么cache反而没什么卵用,说不定还会更耗时!
再比如我的解析框架做了cache,解析的速度非常之快,是不是你就敢大胆的在主线程进行所有的JSON解析?你就不怕有大个的JOSN吗?
如果做了cache,并且在异步线程解析,岂不是更快?是的,的确会快一些,那个差别你是感受不到的,为了你感受不到的快感而去浪费很长的时间层层遍历属性做个cache,这是不值得的,不知道你是否同意?
这是我的做的测试:
NSDictionary *userInfoDic = [self readUserInfo];
[self testCount:100000 work:^{
UserInfoModel *uModel = [UserInfoModel instanceFormDic:userInfoDic];
}];
// 10000 次转换耗时:0.51412s
// 100000 次转换耗时:4.61152s
也就是说使用 JSONUtil 转换一个嵌套4层(里面也有数组)的 model ,转换一次需要: 0.0514ms.
遍历终究是要做的,有两种做法,一种是遍历 model的所有属性,另一种是遍历 服务器返回的json;究竟如何选择,困扰了我许久,最后我选择了后者,因为前者的代价较高,需要层层向上遍历,最可恶的是递归的出口不好把握,因此选择了后者规避了这个问题!
另一方面选择后者是因为,根据我解析的习惯是,服务器定义的字段我都会解析,但是有的字段服务器可能不返回,如果按照前者的遍历方式,会增加几次多余的遍历;当然我否定使用后者不会出现多余的遍历,这个定义model的习惯和服务器返回json的空值都有关系!
1.0 必须继承 QLBaseModel 父类
1.0.1 在属性命名上有限制,对于子model必须自定义属性名才行
1.0.2 去掉了必须自定义属性名的限制
2.0 则去掉了必须继承父类的限制,改名为 JSONUtil
2.1 增加匹配自动类型功能,比如服务器返回的 Number,客户端定义的是 String,那么会帮你自动转为 String
2.2 公司项目也使用这个库,命名规范需要,统一加上 sl(SL)前缀
2.3 项目重构,将该库提取到了通用库中,SL 前缀改为 SC
2.4 支持 CocoaPods 集成
2.4.1 清理没用的方法
2.4.2 当服务器返回数据不能转化为 Number 时,不使用 KVC 赋值
2.4.3 开始在 OS X 平台测试使用
2.4.4 增删类别方法,增加警告信息,创建Model更加省心
做了类型自动映射后,sc_valueNeedTransfer 显得鸡肋,因此去掉了
- (void)sc_valueNeedTransfer; 方法。
新增
- (id)sc_key:(NSString *)key beforeAssignedValue:(id)value;
//增加警告信息
2018-11-27 18:30:17.844219+0800 SCJSONUtilDemo[91682:1947248] ⚠️ UserInfoModel 类没有解析 test 属性;如果客户端和服务端key不相同可通过sc_collideKeysMap 做映射!value:testValue
2018-11-27 18:30:17.844351+0800 SCJSONUtilDemo[91682:1947248] ⚠️⚠️ DataInfoModel 类的 cars 属性没有指定model类名,这会导致解析后数组里的值是原始值,并非model对象!可以通过 sc_collideKeyModelMap 指定 @{@"cars":@"XyzModel"}
2.4.5 增加类别方法,可动态做 key-value 映射
2.4.6 增加类别方法,可自定义解析过程
2.4.7 修复通过 KVC 给标量赋值为 nil 时导致的崩溃
2.4.8 修复没有配置 Model 名时,解析完毕后,属性值为nil问题
2.4.9 当服务器返回值是数组类型,而 Model 属性不是数组类型时,不进行解析
2.5.0 支持解析 long long 类型,丰富测试 case
2.5.1 支持 fileURL 类型,丰富测试 case
2.5.2 增加日志控制开关,更加灵活
2.5.3 重构解析过程,支持 keypath 映射
2.5.4 支持 Model 转 json;提供 JSON2String 工具方法
2.5.5 修复服务端返回字段是 description,hash 等只读属性字段时的崩溃
2.5.6 解决 Xcode14 找不到 libarclite_iphoneos.a 问题
2.5.7 支持 tvos 平台