JSONModel
JSONModel 一个解析 JSON 数据的开源库,可以将 JSON 数据直接解析成自定义的 model ,其中对数据类型的检查和对数据类型的转换比较贴心。最近在项目中使用了以后觉得确实方便很多,推荐给大家。(PS:由于实现的原理在swift中并不支持,所以swift代码了解就可以)。
国际惯例介绍下背景,曾经有一段时间一直要写接口,自然少不了解析获得的 JSON 数据,觉得真的是一种非常机械重复的过程,懵懵懂懂的写一个基于 runtime 的解析器,感觉用起来方便了不少,但是在错误处理和一些特殊情况下使用起来还是觉得有一些不灵活。多年以后偶然的机会又遇到了相同的工作,解析 JSON 数据,这次选择了相对讨巧的办法,使用成熟的开源库来解决。想起那句话“知其然,知其所以然”,决定深入的分析下,把我的理解也分享给大家。
先从使用说起吧,使用 JSONModel 非常简单,只需要将你的 model 类继承自 JSONModel ,而同时 model 中的属性名又恰巧可以和 JSON 数据中的 key 名字一样的话,那么非常恭喜你,你的工作已经完成90%。如果你还有特殊需求实际上写起来也非常方便,我觉得完全可以覆盖日常90%的工作。其他的功能我们会在分析源码的时候看到。
JSONModel 不只使用非常方便而且还会帮你检查 JSON 数据的完整性,如果 JSON 数据不完整的话是要返回 nil 的。它还提供了基本的数据类型转换,比如服务器错将数字传成字符串的话 JSONModel 也会帮你转换成你期望的类型。
先看一下文件结构,无视掉网络相关的类是这样的
既然我们是继承自JSONModel,那我们就看看JSONModel
头文件分成三部分: 1) Property protocol 2) AbstractJSONModelProtocol 3) JSONModel
第一部分 Property protocol
- @protocol Ignore
- @end
复制代码
我们看到的只是4个空的协议 Ignore, Optional, Index, ConvertOnDemand ,分别对应的四种使用方法,忽略、可选、排序、延迟加载,粗看起来貌似都是空的协议没有什么实际的使用价值,但是我觉得正是作者代码精彩之处,我们都知道在 runtime 系统中,每一个对象实例都有一个isa指针(PS:新的 runtime 命名可能有些变化,但是原理相通)指向的是该实例变量的 Class 对象,进而得到该实例对象的所有信息。而 JSONModel 正是利用 runtime 系统的这个特性进行解析数据的,在Class对象中我们可以获得属性的相关描述也包括了其符合的协议,这样这四个空协议就起到了画龙点睛的作用,灵活度直线提高。比如在解析JSON的时候我们发现其某些属性符合Igonre就不会解析该属性,其他的协议同理。美中不足由于swift中对这种机制支持的并不友好所以JSONModel应该不太适用。
还有最后一点作者非常贴心的实现两个Category为了防止编译器的警告,大家也可以参考一下这种写法。
第二部分 AbstractJSONModelProtocol
是 JSONModel 符合的抽象协议。如果你的类也符合该协议, 可以理解于是和JSONModel一样的,可以将不继承自 JSONModel 的类当作属性一起解析,一般还是推荐继承自 JSONModel 不然里面很多细节都要你自己去实现。
第三部分 JSONModel
- -(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
- -(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
- -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
- -(instancetype)initWithData:(NSData *)data error:(NSError **)error;
复制代码
四个初始化方法,实际上最后都是使用initWithDictionary来实现的。具体实现可以参考代码
顺便提一下instancetype这个类型是苹果为了解决我们在继承的时候,返回类型的问题,如果你没有没有遇到或者不了解可以注意下。这样写避免了一直被诟病的反悔 id 类型,感兴趣可以自己查一下相关的资料。
还有一些方便的方法暂时不提,还有几个可以重载的方法.
- +(JSONKeyMapper*)keyMapper;
复制代码
keyMapper 这个是解决JSON中的key和属性名字对应不上时使用的。只对该类生效
setGlobalKeyMapper 这个mapper和keyMapper作用一样只是对所有的JSONModel的子类都生效的一个对应关系
propertyIsOptional 和propertyIsIgnored 是协议的方法版,设想你有1000个属性要写明这两个协议你会不会疯掉。也许直接返回一个YES或者NO就能解决问题对吧。
举个例子
命名自动匹配
{
"id": "123",
"name": "Product name",
"price": 12.95
}
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString* name;
@property (assign, nonatomic) float price;
@end
@implementation ProductModel
@end
模型嵌套 (模型包含其他模型)
{
"order_id": 104,
"total_price": 13.45,
"product" : {
"id": "123",
"name": "Product name",
"price": 12.95
}
}
@interface OrderModel : JSONModel
@property (assign, nonatomic) int order_id;
@property (assign, nonatomic) float total_price;
@property (strong, nonatomic) ProductModel* product;
@end
@implementation OrderModel
@end
模型集合
{
"order_id": 104,
"total_price": 103.45,
"products" : [
{
"id": "123",
"name": "Product #1",
"price": 12.95
},
{
"id": "137",
"name": "Product #2",
"price": 82.95
}
]
}
@protocol ProductModel
@end
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString* name;
@property (assign, nonatomic) float price;
@end
@implementation ProductModel
@end
@interface OrderModel : JSONModel
@property (assign, nonatomic) int order_id;
@property (assign, nonatomic) float total_price;
@property (strong, nonatomic) NSArray<ProductModel>* products;
@end
@implementation OrderModel
@end
注意: 尖括号后 NSArray 包含一个协议. 这跟Objective-C原生的泛型不是一个概念. 他们不会冲突, 但对于JSONModel来说,协议必须在一个地方声明.
key映射
{
"order_id": 104,
"order_details" : [
{
"name": "Product#1",
"price": {
"usd": 12.95
}
}
]
}
@interface OrderModel : JSONModel
@property (assign, nonatomic) int id;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSString* productName;
@end
@implementation OrderModel
+(JSONKeyMapper*)keyMapper
{
return [[JSONKeyMapper alloc] initWithDictionary:@{
@"order_id": @"id",
@"order_details.name": @"productName",
@"order_details.price.usd": @"price"
}];
}
@end
设置全局键映射(应用于所有model)
[JSONModel setGlobalKeyMapper:[
[JSONKeyMapper alloc] initWithDictionary:@{
@"item_id":@"ID",
@"item.name": @"itemName"
}]
];
设置下划线自动转驼峰
{
"order_id": 104,
"order_product" : @"Product#1",
"order_price" : 12.95
}
@interface OrderModel : JSONModel
@property (assign, nonatomic) int orderId;
@property (assign, nonatomic) float orderPrice;
@property (strong, nonatomic) NSString* orderProduct;
@end
@implementation OrderModel
+(JSONKeyMapper*)keyMapper
{
return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
}
@end
可选属性 (就是说这个属性可以为null或者为空)
{
"id": "123",
"name": null,
"price": 12.95
}
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString<Optional>* name;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSNumber<Optional>* uuid;
@end
@implementation ProductModel
@end
忽略属性 (就是完全忽略这个属性)
{
"id": "123",
"name": null
}
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString<Ignore>* customProperty;
@end
@implementation ProductModel
@end
设置所有的属性为可选(所有属性值可以为空)
@implementation ProductModel
+(BOOL)propertyIsOptional:(NSString*)propertyName
{
return YES;
}
@end
使用JSONModel自带的 HTTP 请求
//add extra headers
[[JSONHTTPClient requestHeaders] setValue:@"MySecret" forKey:@"AuthorizationToken"];
//make post, get requests
[JSONHTTPClient postJSONFromURLWithString:@"http://mydomain.com/api"
params:@{@"postParam1":@"value1"}
completion:^(id json, JSONModelError *err) {
//check err, process json ...
}];
将model转化为字典或者json格式的字符串
ProductModel* pm = [[ProductModel alloc] initWithString:jsonString error:nil];
pm.name = @"Changed Name";
//convert to dictionary
NSDictionary* dict = [pm toDictionary];
//convert to text
NSString* string = [pm toJSONString];
自定义数据的转换
@implementation JSONValueTransformer (CustomTransformer)
- (NSDate *)NSDateFromNSString:(NSString*)string {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:APIDateFormat];
return [formatter dateFromString:string];
}
- (NSString *)JSONObjectFromNSDate:(NSDate *)date {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:APIDateFormat];
return [formatter stringFromDate:date];
}
@end
自定义处理指定的属性
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString* name;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSLocale *locale;
@end
@implementation ProductModel
// Convert and assign the locale property
- (void)setLocaleWithNSString:(NSString*)string {
self.locale = [NSLocale localeWithLocaleIdentifier:string];
}
- (NSString *)JSONObjectForLocale {
return self.locale.localeIdentifier;
}
@end
自定义JSON校验
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString* name;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSLocale *locale;
@property (strong, nonatomic) NSNumber <Ignore> *minNameLength;
@end
@implementation ProductModel
- (BOOL)validate:(NSError *__autoreleasing *)error {
BOOL valid = [super validate:error];
if (self.name.length < self.minNameLength.integerValue) {
*error = [NSError errorWithDomain:@"me.mycompany.com" code:1 userInfo:nil];
valid = NO;
}
return valid;
}
@end
其他:参考网址:http://blog.csdn.net/sinat_28032633/article/details/51496288
http://www.cocoachina.com/ios/20150104/10821.html
JSONModel的更多相关文章
- 【iOS】在Swift中使用JSONModel
前言 首先所有的Model还是使用oc来写——看到这一句是不是想关网页了- - #,在swift里面直接写一直报错所以就将就用oc来写了,这里主要是分享一下搭配Alamofire使用的经验. 声明 欢 ...
- iOS JsonModel 的使用
本文转自:http://blog.csdn.net/smking/article/details/40432287 下面讲一下JSONModel的使用方法. @inteface MyModel : J ...
- JSONModel对架构的影响及解决方案
越来越多的项目使用CocoaPods,使用CocoaPods很有可能会用过JSONModel. JSONModel是个很强大的库,只要根据JSON定义好对应的类并继承JSONModel,就可以把JSO ...
- JSONModel 嵌套字典数组 JSONModel nest NSDictionary NSArray
JSONModel 嵌套字典数组 JSONModel nest NSDictionary NSArray
- iOS中JSONModel的使用
iOS中JSONModel的使用 流弊的JSON数据模型框架 https://github.com/jsonmodel/jsonmodel 版本 1.3.0 如果你喜欢JSONModel,并且使用 ...
- JSONModel - 字符串换转实体类
JSONModel https://github.com/icanzilb/JSONModel/ 一. 获取属性的元数据 const char *attrs = property_getAttrib ...
- JSONModel 遇见关键字为id或者description
像id.description这样的,都是系统自带的,要解析它,得特殊处理一下.我用的是JSONModel { "contentList": [ { "id": ...
- JSONModel的基本使用
JSONModel 是一个库,它能智能并且快速的创建出数据 model,你可以在你的 iOS 项目或者 OSX 项目上使用它. 使用前准备 添加 JSONModel 到你的工程中 1.需要的环境: A ...
- CocoaPods 报错 [!] Error installing JSONModel
pod install p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #34bd26 } span.s1 { } ...
- CocoaPods 报错 [!] The dependency `JSONModel (~> 1.2.0)` is not used in any concrete target.
当用CocoaPods pod install 时出现了下面的错误时: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; col ...
随机推荐
- 31.Intellij idea 的maven项目如何通过maven自动下载jar包
转自:https://blog.csdn.net/u012851114/article/details/81872981 maven项目自动加载jar包 所需工具如下: Intellij IDEA 1 ...
- eclipse工作空间配置导出
由于工作与学习的需求,需要使用不同的工作空间.而eclipse的新建工作空间其他以前的配置都没有继承过来,那么就得重新配置一遍. 经过学习其他前辈们的经验与自己的摸索总结一下3种方法: 方法一:使用e ...
- px、em、rem、vw、vh、vm、rpx这些单位的
px是像素 em是参考父元素的font-size的倍数 rem是参考根元素的font-size 常用于响应式,一般会让html的font-size:625%,body的大小为.16rem.这样1rem ...
- 洛谷 P1957 口算练习题
洛谷 P1957 口算练习题 题目描述 王老师正在教简单算术运算.细心的王老师收集了i道学生经常做错的口算题,并且想整理编写成一份练习. 编排这些题目是一件繁琐的事情,为此他想用计算机程序来提高工作效 ...
- Windows学习总结(3)——成为电脑高手必备的cmd命令大全
曾经看电影和电视里面电脑黑客快速敲击电脑键盘,一行行命令在电脑屏幕闪过,一个回车过后,一排排英文象走马灯一样在屏幕上转瞬即逝,那才是我们梦寐以求的高手,有木有!实际上,不光是黑客和系统维护人员,一般的 ...
- 怎样从Cortex-m向STM32移植使用SPI接口协议
/*************************************************************************************************** ...
- Oracle APEX 4.2公布RESTful Webservice
Purpose This tutorial covers creating a RESTful Web Service and accessing the Web Service through an ...
- 微软自拍应用iOS版公布
微软自拍(Microsoft Selfie)主要是支持自拍后还能加强自拍效果的功能. 只是和其它自拍应用不同的是.Microsoft Selfie 利用了机器学习来增强照片,应用会"考虑年龄 ...
- mysql 不能启动的两种错误提示及解决方法
在linux系统中安装mysql服务器详细步骤并解决ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using passw ...
- 22. Node.Js Buffer类(缓冲区)-(二)
转自:https://blog.csdn.net/u011127019/article/details/52512242