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 ...
随机推荐
- html5中的容器标签和文本标签
html5中的容器标签和文本标签 html中的容器级标签和文本级标签,css中的块级元素和行内元素是我们常常拿来比较的四个名词(行内块级暂时先不考虑). 容器标签 容器级的标签可以简单的理解为能嵌套其 ...
- amaze ui中的icon button
amaze ui中的icon button 说明几点: 1.链接效果 连接效果的本质一般都是a标签,好像很多button的链接效果都是用的a标签,submit表单提交或者button的type为sub ...
- 如何才能正确的关闭Socket连接
从TCP协议角度来看,一个已建立的TCP连接有两种关闭方式,一种是正常关闭,即四次挥手关闭连接:还有一种则是异常关闭,我们通常称之为连接重置(RESET).首先说一下正常关闭时四次挥手的状态变迁,关闭 ...
- java为什么要定义接口等相关解释
1.接口的作用是实现多重继承 因为只能继承一个类(规定的) 2.一个类只能继承一个父类,但是可以实现一个或多个接口 3.abstract关键词能让你在类里创建一个或多个没有定义的方法—你给出接口,但 ...
- 20亿与20亿表关联优化方法(超级大表与超级大表join优化方法)
记得5年前遇到一个SQL.就是一个简单的两表关联.SQL跑了几乎相同一天一夜,这两个表都非常巨大.每一个表都有几十个G.数据量每一个表有20多亿,表的字段也特别多. 相信大家也知道SQL慢在哪里了,单 ...
- 将bat批处理命令文件固定到任务栏
将bat批处理命令文件固定到任务栏第一种方法:使用链接工具http://www.xstui.com/read/3451.在任务栏点击右键,移动到工具栏,勾选链接工具2.你会在通知栏左侧看到链接字样,将 ...
- 用jersey写 java restfull web services 输出xml格式数据
1 logic package com.toic.rest; import com.toic.model.Folder; import java.util.logging.Logger; import ...
- asp.net 前后台数据交互方式(转)
https://blog.csdn.net/luckyrass/article/details/38758007 一.前台直接输出后台传递的数据 后台代码: // .aspx.cs public st ...
- 设计模式六大原则(四):接口隔离原则(Interface Segregation Principle)
接口隔离原则(ISP)定义: 客户端不应该依赖它不需要的接口:一个类对另一个类的依赖应该建立在最小的接口上. 问题由来: 类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不 ...
- 词向量 word2vec
看的这一篇的笔记 http://licstar.net/archives/328 看不太懂. 要学的话,看这里吧,这里把一些资料做了整合: http://www.cnblogs.com/wuzhitj ...