【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,都不会被释放。


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/141623.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文件上传 [SUCTF 2019]CheckIn1

打开题目 我们用cmd curl --head url 查看网站使用的是什么服务器 此题用的是openresty&#xff0c;OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台 我们上传php&#xff0c;phtml的一句话木马都显示不合法 那我们试试传a.jpg的一句话木马 显示我们一句话木马内容里面…

为什么打开idea时,没有启动页面,如何解决?

更新idea2021.2后&#xff0c;当双击idea打开时&#xff0c;发现没有启动界面&#xff0c;直接进入IDEA界面&#xff0c;中间等待时间&#xff0c;让人误以为没有打开idea成功&#xff0c;使得多次点击idea图标。 解决方案就是 在idea界面菜单栏中找到帮助&#xff08;Help)&a…

“新KG”视点 | 王文广——图模互补:知识图谱与大模型的共生新模式

OpenKG 大模型专辑 导读 知识图谱和大型语言模型都是用来表示和处理知识的手段。大模型补足了理解语言的能力&#xff0c;知识图谱则丰富了表示知识的方式&#xff0c;两者的深度结合必将为人工智能提供更为全面、可靠、可控的知识处理方法。在这一背景下&#xff0c;OpenKG组织…

总台农业农村节目中心“金穗行动17联县” 融合传播活动走进河北省高碑店市

强国必先强农&#xff0c;农强方能国强。今年是全面贯彻落实党的二十大精神的开局之年&#xff0c;是全面推进乡村振兴、加快建设农业强国的关键之年。 11月8日&#xff0c;总台农业农村节目中心“金穗行动17联县”融合传播活动走进河北省保定市高碑店市&#xff0c;举办了《CC…

Java-多线程基础篇

前言&#xff1a; 以下是看马老师的视频以及自己阅读《Java多线程编程实战指南》所总结的基础内容&#xff0c;只是个人理解&#xff0c;如有不对还请大家指正。 1.线程的概念&#xff1a; 来自于百度百科&#xff1a;线程是独立调度和分派的基本单位。在Unix System V及Sun…

软件测试之蚂蚁金服面试题,含答案

对于想要去面试的小伙伴们看过来&#xff0c;小编为大家总结了蚂蚁的面试题&#xff0c;其实很多面试题很多公司都差不多。主要看自己如何灵活回答&#xff0c;大家可以灵活回应。希望对大家有所帮助。 一&#xff1a;基础部分&#xff1a; Q1. 什么是软件测试&#xff1f; 答…

怎么在web显示模型的动态应力图?

要在网页上显示模型的动态应力图&#xff0c;需要执行几个步骤。动态应力图通常涉及有限元分析 (FEA) 模拟中的应力和应变数据的可视化&#xff0c;可以使用 Python、JavaScript 等工具以及 Three.js、Plotly 或 D3.js 等库来渲染图一个网页。以下是该过程的概述&#xff1a; …

实施MES管理系统项目的三个重要阶段

在现今这个高速发展的时代&#xff0c;每个企业都追求更高的生产效率和更精准的管理。MES管理系统作为助力企业实现这些目标的重要工具&#xff0c;其实施与运营显得尤为关键。以下是我们在实践中总结出的经验与建议&#xff0c;希望为准备引入MES管理系统的企业提供有价值的参…

代码随想录算法训练营第20天|654.最大二叉树 617.合并二叉树 700.二叉搜索树中的搜索 98.验证二叉搜索树

JAVA代码编写 654. 最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树…

linux时间同步

搭建集群时&#xff0c;都会先设置时间同步&#xff0c;否则会出现多种问题。 方式一&#xff1a; 1.安装ntp软件 yum install -y ntp 2.更新时区 删除原有时区&#xff1a;sudo rm -f /etc/localtime 加载新时区&#xff1a;sudo ln -s /usr/share/zoneinfo/Asia/Shangh…

快来看呦!制作3D翻页产品宣传册原来这么受欢迎!

制作3D翻页产品宣传册不但使产品在表达效果上看上去更为绚丽多彩&#xff0c;并且具备比较强的立体视觉效果&#xff0c;增加大家浏览观看的吸引力&#xff0c;而且还便于传播&#xff0c;短时间里增加品牌的影响力。 那么&#xff0c;我们应该如何制作3D翻页产品宣传册&#x…

reticulate | R-python调用 | 安装及配置 | conda文件配置

reticulate | R-python安装及配置 | conda文件配置 1. 基础知识2. 安装reticulate from CRAN3. 包含了用于Python和R之间协同操作的全套工具&#xff0c;在R和Rstudio中均可使用4. 配置python环境4.1 4种环境配置方式4.2 miniconda 环境install_miniconda()报错一install_minic…

企业大数据治理管理平台解决方案:PPT全文33页,附下载

关键词&#xff1a;数据治理解决方案&#xff0c;大数据治理&#xff0c;数据治理的目的和意义 一、数据治理定义 数据治理是指根据数据全生命周期、数据整体流向&#xff0c;将数据作为企业资产进行整体管控、人员绩效评判和风险管理工作的整套治理体系。数据治理旨在保障企…

Java 之集合框架的详细介绍

文章目录 总的介绍1. **Collection 接口**2. **List 接口**3. **Set 接口**4. **Map 接口**5. **HashMap、LinkedHashMap、TreeMap**6. **Queue 接口**7. **Deque 接口** ArrayList 类1. **创建 ArrayList&#xff1a;**2. **添加元素&#xff1a;**3. **插入元素&#xff1a;*…

Antv/G2 折线图 使用 DataSet 进行数据排序

DataSet 文档 G2 3.2 DataSet 文档 安装 浏览器引入 可以通过 <script> 标签引入在线资源或者本地脚本&#xff1a; <!-- 引入在线资源 --> <script src"https://unpkg.com/antv/data-set"></script><!-- 引入本地脚本 --> <sc…

Linux学习第41天:Linux SPI 驱动实验(二):乾坤大挪移

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图如下&#xff1a; 二、I.MX6U SPI主机驱动分析 主机驱动一般都是由SOC厂商写好的。不作为重点需要掌握的内容。 三、SPI设备驱动编写流程 1、SP…

软件测试面试,一份八股文足矣(含文档)

前言 在我认为&#xff0c;对于测试面试以及进阶的最佳学习方法莫过于刷题博客书籍视频总结&#xff0c;前几者我将淋漓尽致地挥毫于这篇博客文章中&#xff0c;至于总结在于个人&#xff0c;实际上越到后面你会发现面试并不难&#xff0c;其次就是在刷题的过程中有没有去思考…

Topaz Photo AI for Mac/win(人工智能降噪软件) 完美兼容激活版

Topaz Photo AI是一款基于人工智能的照片编辑软件&#xff0c;具有革命性的功能。它提供了强大的工具和技术&#xff0c;让用户能够编辑照片而不降低质量。该软件具备高清晰度效果、降噪和自动照片润色工具&#xff0c;能够帮助用户制作令人惊叹的照片。 它包括复杂的锐化算法…

DNS域名解析

目录 1.概述 1.1产生原因 1.2作用 1.3连接方式 1.4因特网的域名结构 1.4.1拓扑 1.4.2分类 1.4.3域名服务器类型划分 2. DNS域名解析过程 2.1分类 2.2解析图 2.2.2过程分析 3.搭建DNS域名解析服务器 3.1.概述 3.2安装软件 3.3bind服务中三个关键文件 3.4主配置…

Matplotlib的使用方法

Matplotlib是Python最著名的绘图库&#xff0c;它提供了一整套和Matlab相似的命令API&#xff0c;十分适合交互式地进行制图。而且也可以方便地将它作为绘图控件&#xff0c;嵌入到GUI应用程序中。Matplotlib能够创建多数类型的图表&#xff0c;如条形图、散点图、条形图、饼图…