博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Decoding之Json解析
阅读量:5967 次
发布时间:2019-06-19

本文共 5114 字,大约阅读时间需要 17 分钟。

今天介绍一个最新Xocde9后,Foundation中更新的一个比较实用及有意思的Decoding功能。

在和网络交互的过程中,我们通过网络请求获得到的响应数据绝大多数都是json格式的数据,而我们通常会将获得的json数据转换成对应的Model模型数据,从而方便我们使用,而这个从json到Model的过程还是有点小麻烦的,一般我们我们可以手动自己解析,也可以通过三方库去解析,方式还是很多的。 在objective-c中,由于OC不是强类型的语言,所以在我们自定义的Model和json数据之间,对具体字段的类型不会有特别严格的类型要求,比如json中的不确定的数据类型的字段,我们可以在Model中用NSString类型的字段去接受,然后通过setValuesForKeysWithDictionary()去赋值,只需要在具体使用的时候去注意就好了。

但是在Swift中这个办法行不通了,因为Swift是强类型的语言,这就造成了,在Swift中如果json数据中某个字段的类型和我们自定义的Model中的字段类型不匹配,同样的用setValuesForKeysWithDictionary()方法去解析json数据就会造成崩溃,如果接口不靠谱,则会因为字段类型不匹配造成很大的麻烦。现在苹果官方给大家提供了一个非常方便实用的方式来解析json数据,具体如下:(终于来到关键了...) 现在我们有一段json数据如下:

{   "status":"ok",    "sources":[               {                   "id":"abc-news-au",                   "name":"ABC News (AU)",                   "description": "Australia's most trusted source of local, national and world news. Comprehensive, independent, in-depth analysis, the latest business, sport, weather and more.",                   "url": "http://www.abc.net.au/news",                   "category": "general",                   "language": "en",                   "country": "au",                   "urlsToLogos": {                   "small":"",                   "medium":"",                   "large":""                   },                   "sortBysAvailable": ["top"]               },               {                   ....               }复制代码

从这段json格式数据我们可以看到,最外层是一个字典,sources字段是一个包含字典的数组,现在我们需要获得sources字段中的数据

  • 1 创建名为SourceModel的模型类,给这个类中添加我们需要的对应json数据中的属性,通常为了通过setValuesForKeysWithDictionary()来获得json中的数据,我们需要将模型中的属性名字和json数据中字段名字1对1对应
class SourceModel:NSObject {   let id: String   let name: String   let category: String   let description: String      init(_ id: String, name: String, category: String, description: String) {       self.id = id       self.name = name       self.category = category       self.overview = description   }}复制代码

通常来说我们的模型基本创建完成(由于后面我要对这个属性用到KVO所以要继承自NSObject),现在为了能够用我们的Decoding新魔法,我们对模型进行初步改造,改造后如下:

//1class SourceModel:NSObject, Codable {    let id: String    let name: String    let category: String    let overview: String   //2     enum CodingKeys: String, CodingKey {        case id        case name        case category        //3        case overview = "description"    }    //4    init(_ id: String, name: String, category: String, overview: String) {        self.id = id        self.name = name        self.category = category        self.overview = overview    }}复制代码

具体步骤:

1.遵守Codable协议,Codable协议是由Encodable和Decodable两个协议组成的,遵守这个协议后,编译器会自动帮我们生成必要的Encodable和Decodable方法

2.由于模型继承自NSObject,而 NSObject中存在名为description的属性,所以子类中不能再有同样的属性,这里我们需要将description进行更名overview(也可能就任性的想改个顺眼的名字),现在你想要用overview去匹配json中的description,所以你要实现//2中的方法,同时做//3样的更改,这是告诉编译器你想要更改字段名字的诉求,如果你不提供这个方法,系统会自动生成这个方法,如果你实现了这个方法,系统会访问你的方法,并按照其中的规则来匹配对应的字段。

注意://2处提供的方法,一定要严格按照这个格式要求来

  • 2接着我们可以创建一个临时的中间数据结构来方便处理数据(后面大家就知道我为什么要这样了)
//中间结构struct Respone: Codable {        var sources: [SourceModel]?}复制代码
//解析数据,参数1为你需要提供的数据类型,参数2为网络请求下来的Data数据let respone = try? JSONDecoder().decode(Respone.self, from: data)复制代码

经过上面的一段代码你就会发现神奇的事情放生了,在respone实例的sources中含有已经解析出来的[SourceModel]数据(我代码中提供的应该是72个SourceModel元素组成的数组),到这里json的解析已经完成了。这里Respone结构体中的变量名sources一定要和json数据中的字段名字相对于,如果你想解析json中的status字段,那么你可以在Respone结构体重添加名为status的变量,如果想要改名则和SourceModel中的操作一样。

由于获得的为可选项,同样的我们也可以将上面的代码完善:

guard let sources = try? JSONDecoder().decode(Respone.self, from: data).sources else { return }复制代码

以上详见demo中NewsAPI.swift及SourceModel.swift中的代码

问题及解决办法

在实际解析数据的过程中我们还会碰到其他的问题及需求,这里我把我碰到的想到的列出来给大家参考。

  • 1 如果json中含有的字段,而我不需要,怎么处理

    答:就像例子中的status字段一样,在你的模型中你不声明,该字段就会自动被忽略

  • 2 如果json中某个字段,可能有值也可能没值,怎么处理

    答:在模型中将该字段声明为可选类型

  • 3 我想对某个字段的数据进行特殊处理怎么办,如我想SourceModel在name字段值的前面加上“名字:”(这个问题,我认为现在的处理方式有一点点麻烦)

    答:由于通过Decoding解析数据时,是通过初始化方法创建模型数据的,所以模型中属性的didSet{}方法不被调用,我们不可以在这里对数据进行处理,我们需要在模型数据中的初始化方法中处理这个属性 代码详见ArticleCell.swift

//在初始化方法中处理required init(from decoder: Decoder) throws {    let container = try decoder.container(keyedBy: CodingKeys.self)    let tempName = try container.decodeIfPresent(String.self, forKey: .name)    name = "名字:" + tempName    //下面即使不需要重新处理的数据也要这样的重新的写一遍,这是我认为有点不好的地方    id =  container.decodeIfPresent(String.self, forKey: .id)  }复制代码
  • 4 在解析数据时,如果碰见数据类型不匹配,字段不存在等问题时,我们可以用下面的这个保险的方法,将解析处的代码更改如下,这样既方便调试也防止崩溃(实际代码中可以只写一个catch防止崩溃就好,调试的时候可以都写出来),详见代码NewsAPI.swift中:
do {        guard let sources = try JSONDecoder().decode(Respone.self, from: data).sources else { return }        self.sources = sources    }catch DecodingError.keyNotFound(let key, let context){        print("MissKey:\(key)")        print("Debug description:\(context)")    }catch DecodingError.valueNotFound(let value, let context) {        print("MissValue:\(value)")        print("Debug description:\(context)")     }catch DecodingError.typeMismatch(let type, let context) {        print("MissType:\(type)")        print("Debug description:\(context)")    }catch {            print(error.localizedDescription)    }复制代码

以上就是本人了解到的Decoding解析Json数据的全部总结了,如果有更多的了解,请及时交流,我也会继续完善的。

最后还要说一个小的知识点,如果你看了,你会发现我用了KVO来监听数据的更新,新的KVO的用法比以前友好及方便了(详见demo中的数据刷新方式)。

ps:这个是本人在掘金的第一篇笔记,以前混简书的,因为大家都知道的原因,以后不准备在简书混了...

地址,欢迎下载

转载地址:http://kttax.baihongyu.com/

你可能感兴趣的文章
处理 允许远程协助连接这台计算机 灰色
查看>>
使用Jquery 加载页面时调用JS
查看>>
css+div+jquery弹出层
查看>>
求职相关(链接,不定期更新)
查看>>
pdo 连接数据库 报错 could not find driver 解决方法
查看>>
设计模式之策略模式
查看>>
JVM介绍
查看>>
maya pyside 多个窗口实例 报错 解决
查看>>
Yxcms网站管理系统安装
查看>>
Nginx错误日志(error_log)配置及信息详解
查看>>
Computer-memory
查看>>
redis 实践笔记(初步)
查看>>
背道而驰or殊途同归?区块链与云计算未来趋势
查看>>
Spring整合JMS(四)——事务管理
查看>>
设计模式学习笔记(七)之模板方法模式(Template Method)
查看>>
我的友情链接
查看>>
主流原型工具可用性测试横向比较
查看>>
我的友情链接
查看>>
Guava——使用Preconditions做参数校验
查看>>
iSCSI存储用作Proxmox VE的LVM共享存储
查看>>