当前位置: 首页 > news >正文

【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类
    • JSONModel的基本使用
      • 对于Model集合、层级嵌套类型数据
      • 设置所有属性可选(遵循Optional协议)
      • JSON转换为Model
      • 将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. 场景:该属性不需要从服务器数据中获取
{@"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中,协议除了可以用来规定解析规则外,还可以用来指定 自定义数据类型的解析 ,只是我们需要自己定义一个协议,名称与自定义类名一致, 示例如下:

@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> *)

@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协议说明

@implementation TestModel+ (BOOL) propertyIsOptional:(NSString *)propertyName {return YES;;
}@end
//... ...其余两个类同理

JSON转换为Model

//LatestStoriesModel* latestStoriesModel = [[LatestStoriesModel alloc] initWithData: data error: nil];
TestModel* model = [[TestModel alloc] initWithDictionary: responseObject error: nil];

将Model导出成字典、字符串

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

设置下划线自动转驼峰

a. 自定义把下划线字段解析为驼峰命名属性
b. 场景:服务器数据返回下划线命名字段可为Model中以驼峰命名的属性相应的赋值
c. mapperFromUpperCaseToLowerCase 大写转小写

 {"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

实例解析

- (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以及跟它同一层级的数据时,用点语法或键值直接访问会报错,像这样:
请添加图片描述
因为在该页面下我们没有事先声明,此处声明一下即可:
请添加图片描述


如果将声明写成属性:

@property (nonatomic, copy) StoriesModel *stories;
  • 将访问到的testModel.stories[0]赋给刚才声明的属性,因为是在block中进行的操作,所以不能直接使用_stories = testModel.stories[0];

请添加图片描述

  • 在代码中使用_stories时,编译器将用self->_stories替换代码,并且如果在块内使用它,则该块将捕获self自身而不是stories本身。 警告只是为了确保开发人员了解此行为。
	self->_stories = testModel.stories[0];NSLog(@"%@",self->_stories.title);

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

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


  • 打印结果:
    请添加图片描述

相关文章:

  • PyCharm鼠标控制字体缩放
  • 阿里云centos7.9乱码问题
  • 【C++】——运算符重载
  • mysql主从复制-使用心得
  • 图片批量编辑器,高效拼接多张图片,释放无限创意!
  • promise多请求并发
  • 【01】Istio-1.17 部署
  • 活动通知邀请函H5页面制作源码系统+动感的背景音乐 自定义你想要的页面 源码完全开源可二开 带完整搭建教程
  • Banana Pi BPI-M5 Boot Log 导出说明
  • 适合孩子写作业的台灯?精选专业的读写台灯
  • 2023nacos源码解读第2集——nacos-server的启动
  • 腾讯待办下架,待办事项提醒怎么设置?
  • std::map使用自定义的数据结构当做key
  • SharePoint-连接Excel
  • 基于单片机微波炉加热箱系统设计
  • canvas 绘制双线技巧
  • Java基本数据类型之Number
  • JAVA之继承和多态
  • PHP面试之三:MySQL数据库
  • webgl (原生)基础入门指南【一】
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 初识 beanstalkd
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 如何设计一个微型分布式架构?
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 阿里云ACE认证学习知识点梳理
  • 进程与线程(三)——进程/线程间通信
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​iOS安全加固方法及实现
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #Spring-boot高级
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • ( 10 )MySQL中的外键
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (06)Hive——正则表达式
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (7)STL算法之交换赋值
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (算法)Game
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (原創) 物件導向與老子思想 (OO)
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (转载)虚函数剖析
  • . Flume面试题
  • .apk文件,IIS不支持下载解决
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .NET Core中的去虚
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .net 提取注释生成API文档 帮助文档