【iOS】JSONModel的基本使用

cocoapods的安装和第三方库的配置之前的文章已有涉及,请参考:【iOS】AFNetworking的基本使用【iOS】Masonry库的基本使用

常规解析JSON数据最基础的方法是使用NSJSONSerialization,见这篇文章【iOS】JSON解析,这样处理数据的方法会有一些麻烦:

  1. 需要很小心地处理Model属性类型与dictionary中的数据对应类型,比如有一个NSURL* url的值,就需要将dict[@"url"]中的NSString类型转化成NSURL类型,很多时候忘记转化就会导致对象类型不一致
  2. 若赋值的地方比较多,每修改一次属性,就需要把所有赋值的地方进行一次整体的更改,工作重复且枯燥
  3. 有时JSON数据如果有遗漏或者变化,不容易发现,比如上边JSON解析这篇文章中,若JSON数据不包含age,通过integerValue方法就会把值赋为0

使用JSONModel就会自行赋值Model中的属性,相对于常规方法,大大简化了代码量和难度

目录


JSONModel简介

简单来说就是调用第三方开源库JSONModel可以简化NSData ------ JSON ------ Model相互转化这一流程

当我们向服务器发送网络请求之后,通过JSONModel把请求下来的json数据解析成我们自定义的继承于JSONModelXXXModel类,进而转化成我们熟悉的数据结构赋值给对象,供我们进行访问

JSONModel不仅使用非常方便,而且还会检查JSON数据的完整性,如果JSON数据不完整会返回nil

JSONModel还提供了基本的数据类型转换,比如服务器错将数字传成字符串的话,JSONModel也会帮你转换成你期望的类型

核心数据模型JSONModel类

来简单分析一下JSONModel.h声明文件的Property Protocol部分

这些协议里并没有约定任何方法,也不会用来实现,只是作为属性的一种标记

  • 属性添加Ignore协议表示JSONModel不会对这个属性进行解析,使用这种方式来进行本地数据的管理 (属性值可以完全忽略)
    a. 解析时完全忽略ta
    b. 场景:该属性不需要从服务器数据中获取
objectivec 复制代码
{
   @"id":"777",
   @"name":"Jacky",
   @"age":19
}

@interface MyModel : JSONModel

@property (nonatomic, copy)NSString* id;
@property (nonatomic, copy)NSString* name;
@property (nonatomic, assign)NSInteger age;

//一般这个属性都是拼接上去、在本地操作的
@property (nonatomic, copy)NSString<Ignore>* gender;

@end
  • Optional协议表示这个协议是可选的,即JSON数据中如果有这个属性就解析,如果没有就跳过 (属性值可以为空或null)
    a. 某些属性值可以为空
    b. 防止由于服务器返回数据为空导致JSONModel异常(程序崩溃)
  • 可以看到以下两个协议被标记DEPRECATED_ATTRIBUTE,说明已被弃用,下面仅作以记录📝:
    a. ConvertOnDemand协议表示延迟加载 (懒加载) , 可以减少在网络读取时的性能消耗
    b. Index协议的作用是可以直接用索引访问该属性(在一个数组中被索引)

有了这些协议,在声明属性时,我们可以十分容易地设定ta们的解析规则,在JSONModel中,协议除了可以用来规定解析规则外,还可以用来指定 自定义数据类型的解析 ,只是我们需要自己定义一个协议,名称与自定义类名一致, 示例如下:

objectivec 复制代码
@protocol Address : JSONModel
@end

@interface Address : JSONModel
@property (nonatomic, strong)NSString* info;
@end

@interface MyModel : JSONModel

@property (nonatomic, copy)NSString* id;
@property (nonatomic, copy)NSString* name;
@property (nonatomic, assign)NSInteger age;
@property (nonatomic, copy)NSString<Ignore>* gender;

//@property (nonatomic, strong)Address<Address>* address;
@property (nonatomic, strong)NSArray<Address>* address;

@end

如上代码所示,在解析数据时,会直接将address数组中赋值为Address的对象,当然也可以像注释掉的那一行一样直接解析对象

JSONModel的基本使用

首先向服务器请求一个JSON数据 --- 【iOS】简单的网络请求

JSON数据来源:知乎日报API分析

对于Model集合、层级嵌套类型数据

a. 层级嵌套,Model中嵌套其他Model集合,将被嵌套的集合都写成一个类,且只需在同一个类文件中实现实现即可(例如:StoriesTop_Sories

b. 包含其他Model集合的属性需要指定层级类型和自身类型(例如:NSArray<Stories> *)

objectivec 复制代码
@protocol StoriesModel
@end

@protocol Top_StoriesModel
@end

@interface StoriesModel : JSONModel
@property (nonatomic, copy) NSString* image_hue;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* url;
@property (nonatomic, copy) NSString* hint;
@property (nonatomic, copy) NSString* id;

@end

@interface Top_StoriesModel : JSONModel
@property (nonatomic, copy) NSString* image_hue;
@property (nonatomic, copy) NSString* hint;
@property (nonatomic, copy) NSString* url;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* id;

@end

@interface TestModel : JSONModel

@property (nonatomic, copy) NSString *date;
@property (nonatomic, copy) NSArray<StoriesModel> *stories;
@property (nonatomic, copy) NSArray<Top_StoriesModel > *top_stories;

@end

设置所有属性可选(遵循Optional协议)

作用见上述Optional协议说明

objectivec 复制代码
@implementation TestModel

+ (BOOL) propertyIsOptional:(NSString *)propertyName {
    return YES;;
}

@end
//... ...其余两个类同理

JSON转换为Model

objectivec 复制代码
//LatestStoriesModel* latestStoriesModel = [[LatestStoriesModel alloc] initWithData: data error: nil];
TestModel* model = [[TestModel alloc] initWithDictionary: responseObject error: nil];

将Model导出成字典、字符串

objectivec 复制代码
//转化成字典
NSDictionary* dict = [model toDictionary];
//转化成字符串
NSString* string = [model toJSONString];

设置下划线自动转驼峰

a. 自定义把下划线字段解析为驼峰命名属性

b. 场景:服务器数据返回下划线命名字段可为Model中以驼峰命名的属性相应的赋值

c. mapperFromUpperCaseToLowerCase 大写转小写

objectivec 复制代码
 {
   "order_id": 104,
   "order_product" : @"Product#1",
   "order_price" : 12.95
 }

@interface OrderModel : BaseModel
@property (nonatomic, strong) NSString *orderId;
@property (nonatomic, assign) float     orderPrice;
@property (nonatomic, strong) NSString *orderProduct;

@end

@implementation OrderModel
+ (JSONKeyMapper *)keyMapper
{
    return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
}

@end

实例解析

objectivec 复制代码
- (void)requestLatestStories {
    NSString* jsonData = @"https://news-at.zhihu.com/api/4/news/latest";
    jsonData = [jsonData stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    
    NSURL* url = [NSURL URLWithString: jsonData];
    NSURLRequest* request = [NSURLRequest requestWithURL: url];
    
    NSURLSession* session = [NSURLSession sharedSession];
    NSURLSessionTask* task = [session dataTaskWithRequest: request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        TestModel* testModel = [[TestModel alloc] initWithData: data error: nil];
//        NSLog(@"%@", testModel.stories[0].title);
//        NSLog(@"%@", testModel.stories[0][@"title"]);
//        StoriesModel* stories = testModel.stories[0];
//        NSLog(@"%@", stories.title);
        
        NSLog(@"%@", testModel.stories[0]);
    }];
    
    [task resume];
}

注意⚠️这里要访问title以及跟它同一层级的数据时,用点语法或键值直接访问会报错,像这样:

因为在该页面下我们没有事先声明,此处声明一下即可:


如果将声明写成属性:

objectivec 复制代码
@property (nonatomic, copy) StoriesModel *stories;
  • 将访问到的testModel.stories[0]赋给刚才声明的属性,因为是在block中进行的操作,所以不能直接使用_stories = testModel.stories[0];
  • 在代码中使用_stories时,编译器将用self->_stories替换代码,并且如果在块内使用它,则该块将捕获self自身而不是stories本身。 警告只是为了确保开发人员了解此行为。
objectivec 复制代码
	self->_stories = testModel.stories[0];
    NSLog(@"%@",self->_stories.title);

这里涉及一点Block循环引用的知识,编者写过的分析Block文章也只是浅析,现阶段仅简单了解:

Block循环引用问题 首先什么是循环引用呢? 就是两个对象相互持有,在释放时,相互等待释放,造成死循环谁都释放不了,从而内存泄露。 即block作为self的属性时,又在block内部调用了self的属性和方法,block和self相互持有,那么两者的引用计数都至少是1,都不会被释放。


  • 打印结果:
相关推荐
SoraLuna4 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
追风林4 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
yqcoder4 小时前
mac 安装 nodemon
macos
一ge科研小菜鸡4 小时前
macOS开发环境配置与应用开发(详细讲解)
macos
hairenjing11234 小时前
使用 Mac 数据恢复从 iPhoto 图库中恢复照片
windows·stm32·嵌入式硬件·macos·word
2401_865854887 小时前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
zorchp11 小时前
在 MacOS 上跑 kaldi
macos·kaldi
德育处主任11 小时前
Mac和安卓手机互传文件(ADB)
android·macos
土小帽软件测试12 小时前
jmeter基础01-2_环境准备-Mac系统安装jdk
java·测试工具·jmeter·macos·软件测试学习
小百菜14 小时前
dom4j实现xml转map,xml转json字符串
xml·json·xml转map·xml转json