iOS开发 - Swift Codable协议实战:快速、简单、高效地完成JSON和Model转换!

前言
Codable 是 Swift 4.0 引入的一种协议,它是一个组合协议,由 Decodable 和 Encodable 两个协议组成。它的作用是将模型对象转换为 JSON 或者是其它的数据格式,也可以反过来将 JSON 数据转换为模型对象。
Encodable 和 Decodable 分别定义了 encode(to:) 和 init(from:) 两个协议函数,分别用来实现数据模型的归档和外部数据的解析和实例化。最常用的场景就是刚提到的 JSON 数据与模型的相互转换,但是 Codable 的能力并不止于此。
简单应用
在实际开发中,Codable 的使用非常方便,只需要让模型遵循 Codable 协议即可:
struct GCPerson: Codable {var name: Stringvar age: Intvar height: Float // cmvar isGoodGrades: Bool
}
接下来编写数据编码和解码的方法:
func encodePerson() {let person = GCPerson(name: "XiaoMing", age: 16, height: 160.5, isGoodGrades: true)let encoder = JSONEncoder()encoder.outputFormatting = .prettyPrinted // 优雅永不过时,json会好看点哟do {let data = try encoder.encode(person)let jsonStr = String(data: data, encoding: .utf8)textView.text = jsonStrprint(jsonStr as Any)} catch let err {print("err", err)}
}func decodePerson() {let jsonStr = "{\"age\":16,\"isGoodGrades\":1,\"name\":\"XiaoMing\",\"height\":160.5}"guard let data = jsonStr.data(using: .utf8) else {print("get data fail")return}let decoder = JSONDecoder()do {let person = try decoder.decode(GCPerson.self, from: data)print(person)} catch let err {print("err", err)}
}
上面例子的输出:
Optional("{\n \"age\" : 16,\n \"isGoodGrades\" : true,\n \"name\" : \"XiaoMing\",\n \"height\" : 160.5\n}")
GCPerson(name: "XiaoMing", age: 16, height: 160.5, isGoodGrades: false)
应该有眼尖的童鞋是发现了,我将 JSONEncoder 的 outputFormatting 设置为了 prettyPrinted,这会让它输出的时候会美观一下,比如将它们放置在 UITextView 视图中作对比:

这里指的
default是在没有设置outputFormatting的默认情况
CodingKeys 字段映射
如果属性名称与 JSON 数据中的键名不一致,需要使用 Swift 语言中的 CodingKeys 枚举来映射属性名称和键名。CodingKeys 是一个遵循了 CodingKey 协议的枚举,它可以用来描述 Swift 对象的属性与 JSON 数据中的键名之间的映射关系。
struct Address: Codable {var zipCode: Intvar fullAddress: Stringenum CodingKeys: String, CodingKey {case zipCode = "zip_code"case fullAddress = "full_address"}
}
数据编码和解码的方法与前面的大同小异:
func encodeAddress() {let address = Address(zipCode: 528000, fullAddress: "don't tell you")let encoder = JSONEncoder()encoder.outputFormatting = .prettyPrinted // 优雅永不过时,json会好看点哟do {let data = try encoder.encode(address)let jsonStr = String(data: data, encoding: .utf8)textView.text.append("\n\n")textView.text = textView.text.appending(jsonStr ?? "")print(jsonStr as Any)} catch let err {print("err", err)}
}func decodeAddress() {let jsonStr = "{\"zip_code\":528000,\"full_address\":\"don't tell you\"}"guard let data = jsonStr.data(using: .utf8) else {print("get data fail")return}let decoder = JSONDecoder()do {let address = try decoder.decode(Address.self, from: data)print(address)} catch let err {print("err", err)}
}
此时的输出为:
Optional("{\n \"zip_code\" : 528000,\n \"full_address\" : \"don\'t tell you\"\n}")
Address(zipCode: 528000, fullAddress: "don\'t tell you")
从控制台日志可以看出,Address 模型中的的 zipCode 和 fullAddress 属性字段已被替换为 zip_code 和 full_address,值得注意的是,使用 CodingKeys 映射后就只能使用映射后的字段名称。
数据类型匹配
Swift 中的数据类型需要与 JSON 数据中的数据类型匹配,否则将无法正确地进行解码。如果数据类型不匹配,则会进入到 catch 代码块,意味着解码失败。
let jsonStr = "{\"age\":16,\"isGoodGrades\":1,\"name\":\"XiaoMing\",\"height\":160.5}"
在上面的例子中,将 isGoodGrades 的值改为1,此时输出的错误内容为:
err typeMismatch(Swift.Bool, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "isGoodGrades", intValue: nil)], debugDescription: "Expected to decode Bool but found a number instead.", underlyingError: nil))
由此引出,Bool 型只支持 true 和 false,其它一概不认。
注意:只要是其中一个数据字段不能解析,则整条解析失败。
Date 和 Optional 可选类型
在使用 Codable 对 Date 和 Optional 属性进行编解码时,有些细节是需要了解的。
Codable 默认启用的时间策略是 deferredToDate,即从 UTC时间2001年1月1日0时0分0秒 开始的秒数,对应 Date 类型中 timeIntervalSinceReferenceDate 这个属性。比如 702804983.44863105 这个数字解析后的结果是 2023-04-10 07:34:17 +0000。
在这儿把时间策略设置为 secondsSince1970,因为这个会比上面的要常用。我们需将 JSONEncoder 的 dateEncodingStrategy 设置为 secondsSince1970,JSONDecoder 也是相同的设置。
在设置 Optional 可选类型时,在编码时,为空的属性不会包含在 JSON 数据中。在解码时,直接不传或将值设定为 \"null\" / \"nil\" / null 这三种值也能被解析为 nil。
struct Activity: Codable {var time: Datevar url: URL?
}
编码解码的工作:
func encodeActivity() {let activity = Activity(time: Date(), url: URL(string: "https://www.baidu.com"))let encoder = JSONEncoder()encoder.outputFormatting = .prettyPrinted // 优雅永不过时,json会好看点哟encoder.dateEncodingStrategy = .secondsSince1970 // 秒do {let data = try encoder.encode(activity)let jsonStr = String(data: data, encoding: .utf8)textView.text.append("\n\n")textView.text = textView.text.appending(jsonStr ?? "")print(jsonStr as Any)} catch let err {print("err", err)}
}func decodeActivity() {
// let jsonStr = "{\"time\":528000,\"url\":111}" // 即便是 Optional 的属性也要对应的数据类型,否则还是会解析失败let jsonStr = "{\"time\":1681055185}" // Optional类型的属性字段,直接不传也是nil// let jsonStr = "{\"time\":528000,\"url\":null}" // 以下三种也能被解析为nil,\"null\" / \"nil\" / nullguard let data = jsonStr.data(using: .utf8) else {print("get data fail")return}let decoder = JSONDecoder()decoder.dateDecodingStrategy = .secondsSince1970 // 秒do {let activity = try decoder.decode(Activity.self, from: data)print(activity)} catch let err {print("err", err)}
}
此时的输出为:
Optional("{\n \"url\" : \"https:\\/\\/www.baidu.com\",\n \"time\" : 1681057020.835813\n}")
Activity(time: 2023-04-09 15:46:25 +0000, url: nil)
自定义编解码
有时候前后端定义的模型不同时,有可能会需要用到自定义编解码,以此来达成“统一”。
比如我们现在有一个 Dog 模型,sex 字段为 Bool 型,在后端的定义为 0 和 1,此时我们需要将它们给转换起来,可以是 false 为 0,true 为 1。
struct Dog: Codable {var name: Stringvar sex: Bool // 0/false女 1/true男init(name: String, sex: Bool) {self.name = nameself.sex = sex}// 必须实现此枚举,在编码解码方法中需要用到enum CodingKeys: CodingKey {case namecase sex}init(from decoder: Decoder) throws {let container = try decoder.container(keyedBy: CodingKeys.self)self.name = try container.decode(String.self, forKey: .name)// 取出来int后再转换为Boollet sexInt = try container.decode(Int.self, forKey: .sex)sex = sexInt == 1}func encode(to encoder: Encoder) throws {var container = encoder.container(keyedBy: CodingKeys.self)try container.encode(self.name, forKey: .name)// 将sex属性以int类型编码try container.encode(sex ? 1 : 0, forKey: .sex)}
}
在编码的时候将 sex 从 Bool 型转换为 Int 型,解码时则反过来。编解码的工作依旧与前面的大致一样:
func encodeDog() {let dog = Dog(name: "Max", sex: true)let encoder = JSONEncoder()encoder.outputFormatting = .prettyPrinted // 优雅永不过时,json会好看点哟do {let data = try encoder.encode(dog)let jsonStr = String(data: data, encoding: .utf8)textView.text.append("\n\n")textView.text = textView.text.appending(jsonStr ?? "")print(jsonStr as Any)} catch let err {print("err", err)}
}func decodeDog() {let jsonStr = "{\"name\":\"Max\",\"sex\":1}"guard let data = jsonStr.data(using: .utf8) else {print("get data fail")return}let decoder = JSONDecoder()do {let dog = try decoder.decode(Dog.self, from: data)print(dog)} catch let err {print("err", err)}
}
此时的日志输出为:
Optional("{\n \"name\" : \"Max\",\n \"sex\" : 1\n}")
Dog(name: "Max", sex: true)
总结
Codable 是 Swift 中非常方便的一个协议,可以帮助我们快速进行数据的编码和解码,提高了开发效率和代码可读性。当然使用不当也会造成严重的灾难,所以我为大家整理了以下几点使用时的注意事项,希望能对大家有所帮助:
- 嵌套的数据结构也需要遵循
Codable协议。 Bool型只支持true或false。Optional类型修饰的属性字段,直接不传是nil,或将值设定为以下三种也能被解析为nil,\"null\"/\"nil\"/null。- 可以使用自定义的编码器和解码器来进行转换。
相关文章:
iOS开发 - Swift Codable协议实战:快速、简单、高效地完成JSON和Model转换!
前言 Codable 是 Swift 4.0 引入的一种协议,它是一个组合协议,由 Decodable 和 Encodable 两个协议组成。它的作用是将模型对象转换为 JSON 或者是其它的数据格式,也可以反过来将 JSON 数据转换为模型对象。 Encodable 和 Decodable 分别定…...
RabbitMq:Topic exchange(主题交换机)的理解和使用
RabbitMq:Topic exchange(主题交换机)的理解和使用 在RabbitMq中,生产者的消息都是通过交换机来接收,然后再从交换机分发到不同的队列中去,在分发的过程中交换机类型会影响分发的逻辑,下面主要讲解一下主题交换机。 主题交换…...
汽车级36V、4A同步降压转换器MAX20404AFOD/VY、MAX20404AFOC/VY、MAX20404AFOA/VY开关稳压器
MAX20404是小型同步降压转换器,集成了高端和低端开关。这些IC均设计为可在3V到36V的宽输入电压范围内提供高达4A的电流。电压质量可以通过观察PGOOD信号来监测。该器件可以在99%的占空比下运行,非常适合汽车和工业应用。 MAX20404提供可编程输出电压或5…...
C++------利用C++实现二叉搜索树【数据结构】
文章目录 二叉搜索树概念二叉搜索树的操作查找插入删除 二叉搜索树的应用 二叉搜索树 概念 什么是二叉搜索树,二叉搜索树就是指左孩子永远比根小右孩子永远比根大。这个规则适用于所有的子树。 上面的就是一棵二叉搜索树,我们还可以发现这棵树走一个中…...
HotSpot虚拟机之内存模型与线程安全
目录 一、线程内存模型 1. 内存模型 2. 内存模型操作 二、Happens-Before原则 三、Java线程 1. 线程实现方式 2. Java线程状态 四、Java线程安全 1. 线程安全程度 2. 锁优化 五、参考资料 一、线程内存模型 1. 内存模型 内存模型主要目的是定义共享变量的访问规则&…...
TiDB 多集群告警监控-中章-融合多集群 Grafana
作者: longzhuquan 原文来源: https://tidb.net/blog/ac730b0f 背景 随着公司XC改造步伐的前进,越来越多的业务选择 TiDB,由于各个业务之间需要物理隔离,避免不了的 TiDB 集群数量越来越多。虽然每套 TiDB 集群均有…...
【图像分类】基于卷积神经网络和主动学习的高光谱图像分类(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
notepad++ verilog关键字自动补全
新建verilog.xml放在安装目录下 D:\Program Files (x86)\Notepad\autoCompletion <?xml version"1.0" encoding"Windows-1252" ?> <NotepadPlus><AutoComplete><KeyWord name"accept_on" /><KeyWord name"a…...
C语言知识
C语言知识 链接 C语言中的数组初始化是有三种形式的,分别是: (1)数据类型 数组名称[长度n] {元素1,元素2…元素n}; (2)数据类型 数组名称[] {元素1,元素2…元素n}; (3)数据类型 数组名称[长度n]; 数组名称[0] 元素1; 数组名称[1] 元素2; 数组…...
数据结构基础
将节点构建成树 数据的结构逻辑结构集合线性结构树形结构图状结构 存储结构合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如…...
深度学习中数据处理相关的技巧
文章目录 提取隐蔽特征惰性加载数据集类别分布不均衡 提取隐蔽特征 在某些任务中,一些类别的特征可能相对较为罕见或难以捕捉。由于这些特征在数据集中出现的频率较低,模型可能无法充分学习它们,从而导致对这些类别的辨别能力较弱。为了解决…...
wkhtmltopdf 与 .Net Core
wkhtmltopdf 是使用webkit引擎转化为pdf的开源小插件. 其有.NET CORE版本的组件,DinkToPdf,但该控件对跨平台支持有限 。 是由于各系统平台会产生不同的编译结果,故windows上使用.dll,而Linux上的动态链接库是.so 所以你需要在Linux系统上安装相关wkhtmltox软件。 我这里准备了…...
Linux Mint 21.3 计划于 2023 年圣诞节发布
Linux Mint 项目近日公布了基于 Ubuntu 的 Linux Mint 发行版下一个重要版本的一些初步细节,以及备受期待的基于 Debian 的 LMDE 6(Linux Mint Debian Edition)版本。 近日,Linux Mint 项目负责人克莱门特-勒菲弗(Clem…...
腾讯云3年轻量应用服务器2核4G5M和2核2G4M详细介绍
腾讯云轻量应用服务器3年配置,目前可以选择三年的轻量配置为2核2G4M和2核4G5M,2核2G4M和2核4G5M带宽,当然也可以选择选一年,第二年xufei会比较gui,腾讯云百科分享腾讯云轻量应用服务器3年配置表: 目录 腾…...
rabbitmq中的消息确认
如何保证消息被全部消费 应用场景:我们不想丢失任何任务消息。如果一个工作者(worker)挂掉了,我们希望任务会重新发送给其他的工作者(worker)。 为了防止消息丢失,RabbitMQ提供了消息响应&…...
jenkins一键部署github项目
个人目前理解jenkins部署分为两步: 构建项目,如生成jar自动执行sh脚本 如果没有jenkins,我们可能需要将jar移动到服务器,然后执行java -jar跑程序,jenkins可以替代我们执行这些东西,下面从0开始࿰…...
岩土工程安全监测隧道中使用振弦采集仪注意要点?
岩土工程安全监测隧道中使用振弦采集仪注意要点? 岩土工程的安全监测是非常重要的,它可以帮助工程师及时发现可能存在的问题,并及时解决,保障施工进度以及施工质量,保障工程的安全运行。其中,振弦采集仪是…...
第四章nginx组件精讲
nginx配件location匹配的规则和优先级(重点面试题) RUI:统一资源标识符,是一种字符串标识,用于标识抽象的或者物理资源(文件,图片,视频) nginx当中:uri ww…...
LlamaGPT -基于Llama 2的自托管类chatgpt聊天机器人
LlamaGPT一个自托管、离线、类似 ChatGPT 的聊天机器人,由 Llama 2 提供支持。100% 私密,不会有任何数据离开你的设备。 推荐:用 NSDT编辑器 快速搭建可编程3D场景 1、如何安装LlamaGPT LlamaGPT可以安装在任何x86或arm64系统上。 首先确保…...
常见的跨域解决方案
常见的跨域解决方案: 跨域问题可以分为两种情况:前端跨域和后端跨域。以下是针对这两种情况的跨域解决方案: 前端跨域解决方案: JSONP: 适用于前端向不同域名下的服务器请求数据,通过添加回调函数名称来…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
