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

  1. @protocol Ignore
  2. @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

  1. -(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
  2. -(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
  3. -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
  4. -(instancetype)initWithData:(NSData *)data error:(NSError **)error;

复制代码

四个初始化方法,实际上最后都是使用initWithDictionary来实现的。具体实现可以参考代码

顺便提一下instancetype这个类型是苹果为了解决我们在继承的时候,返回类型的问题,如果你没有没有遇到或者不了解可以注意下。这样写避免了一直被诟病的反悔 id 类型,感兴趣可以自己查一下相关的资料。

还有一些方便的方法暂时不提,还有几个可以重载的方法.

  1. +(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的更多相关文章

  1. 【iOS】在Swift中使用JSONModel

    前言 首先所有的Model还是使用oc来写——看到这一句是不是想关网页了- - #,在swift里面直接写一直报错所以就将就用oc来写了,这里主要是分享一下搭配Alamofire使用的经验. 声明 欢 ...

  2. iOS JsonModel 的使用

    本文转自:http://blog.csdn.net/smking/article/details/40432287 下面讲一下JSONModel的使用方法. @inteface MyModel : J ...

  3. JSONModel对架构的影响及解决方案

    越来越多的项目使用CocoaPods,使用CocoaPods很有可能会用过JSONModel. JSONModel是个很强大的库,只要根据JSON定义好对应的类并继承JSONModel,就可以把JSO ...

  4. JSONModel 嵌套字典数组 JSONModel nest NSDictionary NSArray

    JSONModel 嵌套字典数组  JSONModel nest NSDictionary NSArray

  5. iOS中JSONModel的使用

    iOS中JSONModel的使用   流弊的JSON数据模型框架 https://github.com/jsonmodel/jsonmodel 版本 1.3.0 如果你喜欢JSONModel,并且使用 ...

  6. JSONModel - 字符串换转实体类

     JSONModel https://github.com/icanzilb/JSONModel/ 一. 获取属性的元数据 const char *attrs = property_getAttrib ...

  7. JSONModel 遇见关键字为id或者description

    像id.description这样的,都是系统自带的,要解析它,得特殊处理一下.我用的是JSONModel { "contentList": [ { "id": ...

  8. JSONModel的基本使用

    JSONModel 是一个库,它能智能并且快速的创建出数据 model,你可以在你的 iOS 项目或者 OSX 项目上使用它. 使用前准备 添加 JSONModel 到你的工程中 1.需要的环境: A ...

  9. 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 { } ...

  10. 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 ...

随机推荐

  1. 31.Intellij idea 的maven项目如何通过maven自动下载jar包

    转自:https://blog.csdn.net/u012851114/article/details/81872981 maven项目自动加载jar包 所需工具如下: Intellij IDEA 1 ...

  2. eclipse工作空间配置导出

    由于工作与学习的需求,需要使用不同的工作空间.而eclipse的新建工作空间其他以前的配置都没有继承过来,那么就得重新配置一遍. 经过学习其他前辈们的经验与自己的摸索总结一下3种方法: 方法一:使用e ...

  3. px、em、rem、vw、vh、vm、rpx这些单位的

    px是像素 em是参考父元素的font-size的倍数 rem是参考根元素的font-size 常用于响应式,一般会让html的font-size:625%,body的大小为.16rem.这样1rem ...

  4. 洛谷 P1957 口算练习题

    洛谷 P1957 口算练习题 题目描述 王老师正在教简单算术运算.细心的王老师收集了i道学生经常做错的口算题,并且想整理编写成一份练习. 编排这些题目是一件繁琐的事情,为此他想用计算机程序来提高工作效 ...

  5. Windows学习总结(3)——成为电脑高手必备的cmd命令大全

    曾经看电影和电视里面电脑黑客快速敲击电脑键盘,一行行命令在电脑屏幕闪过,一个回车过后,一排排英文象走马灯一样在屏幕上转瞬即逝,那才是我们梦寐以求的高手,有木有!实际上,不光是黑客和系统维护人员,一般的 ...

  6. 怎样从Cortex-m向STM32移植使用SPI接口协议

    /*************************************************************************************************** ...

  7. Oracle APEX 4.2公布RESTful Webservice

    Purpose This tutorial covers creating a RESTful Web Service and accessing the Web Service through an ...

  8. 微软自拍应用iOS版公布

    微软自拍(Microsoft Selfie)主要是支持自拍后还能加强自拍效果的功能. 只是和其它自拍应用不同的是.Microsoft Selfie 利用了机器学习来增强照片,应用会"考虑年龄 ...

  9. mysql 不能启动的两种错误提示及解决方法

    在linux系统中安装mysql服务器详细步骤并解决ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using passw ...

  10. 22. Node.Js Buffer类(缓冲区)-(二)

    转自:https://blog.csdn.net/u011127019/article/details/52512242