Swift使用JSONDecoder处理json数据,实现json序列化和反序列化

Json数据处理是开发中不可获取的一项技能,如果你不会处理json数据,那你离失业就不远了,所以学完了swift基础教程,还是先老老实实学习一下json处理吧,有了这项技能,你才可以继续下一个网络请求阶段的开发,因为网络请求大部分都是依赖json数据传输的。

JSONDecoder

JSONDecoder是Swift标准库中的一个类,用于将JSON数据解码为Swift中的自定义数据类型,或者将swift中的数据类型转化为JSON字符串。它提供了一种简单、类型安全的方式来处理JSON数据,可以将JSON数据转换为Swift中的结构体、类或枚举。

注意几个知识点:

JSON序列化

将swift中的自定义结构体数据转化为json字符串,定义一个Person结构体,创建一个这个结构体实例,然后使用JSONDecoder序列化为String字符串:


// json 序列化
func jsonToStr() {
    print("json 转为字符串")
    struct Person: Codable {
        let name: String
        let age: Int
        let email: String
    }
    // 创建结构体实例
    let per2 = Person(name: "EmployA", age: 18, email: "john@example.com")
    // 创建jsonEncoder
    let jsonEncoder = JSONEncoder()
    // 将结构体实例per2转为Data
    let jsonData = try? jsonEncoder.encode(per2)
    print("jsonData: \(String(describing: jsonData))")
    // 将data转为String
    let jsonString = String(data: jsonData!, encoding: .utf8)!
    print("jsonString: \(jsonString)")
}

JSON反序列化

将Json字符串转化为swift里面的结构体,创建一个json字符串,然后使用JSONDecoder将字符串反序列化为结构体数据:

// json 反序列化
func jsonDecode() {
    print("json 数据解码")
    struct Person: Codable {
        let name: String
        let age: Int
        let email: String
    }
    let jsonStr = """
    {
      "name": "John",
      "age": 30,
      "email": "john@example.com"
    }
    """
    // 将json字符串转换为Data
    let jsonData = jsonStr.data(using: .utf8)
    // JSONDecoder
    let decoder = JSONDecoder()
    // 使用decoder将Data转为结构体
    let per1 = try! decoder.decode(Person.self, from: jsonData!)
    print("person name: \(per1.name)")
    print("person age: \(per1.age)")
    print("persn email: \(per1.email)")
}

完整代码示例:

源代码:

import SwiftUI

func getFood() -> String {
    return ["🍏", "🍎", "🍐", "🍊", "🍌", "🍋", "🍉", "🍇", "🍓", "🫐"].randomElement()!
}

// json 反序列化
func jsonDecode() {
    print("json 数据解码")
    struct Person: Codable {
        let name: String
        let age: Int
        let email: String
    }
    let jsonStr = """
    {
      "name": "John",
      "age": 30,
      "email": "john@example.com"
    }
    """
    // 将json字符串转换为Data
    let jsonData = jsonStr.data(using: .utf8)
    // JSONDecoder
    let decoder = JSONDecoder()
    // 使用decoder将Data转为结构体
    let per1 = try! decoder.decode(Person.self, from: jsonData!)
    print("person name: \(per1.name)")
    print("person age: \(per1.age)")
    print("persn email: \(per1.email)")
}

// json 序列化
func jsonToStr() {
    print("json 转为字符串")
    struct Person: Codable {
        let name: String
        let age: Int
        let email: String
    }
    // 创建结构体实例
    let per2 = Person(name: "EmployA", age: 18, email: "john@example.com")
    // 创建jsonEncoder
    let jsonEncoder = JSONEncoder()
    // 将结构体实例per2转为Data
    let jsonData = try? jsonEncoder.encode(per2)
    print("jsonData: \(String(describing: jsonData))")
    // 将data转为String
    let jsonString = String(data: jsonData!, encoding: .utf8)!
    print("jsonString: \(jsonString)")
}

struct ContentView: View {
    @State var food = "🍏"

    var body: some View {
        VStack {
            Spacer()
            Spacer()
            Spacer()
            Text(food).font(.system(size: 80))
            Spacer()
            Button(action: {
                food = getFood()
            }, label: {
                Text("今天吃啥?")
                    .padding()
                    .foregroundColor(.white)
                    .font(/*@START_MENU_TOKEN@*/ .title/*@END_MENU_TOKEN@*/)
            }).background(.orange)
                .cornerRadius(10)
            Button(action: {
                print("解码json")
                jsonDecode()
            }, label: {
                Text("解码JSON").font(/*@START_MENU_TOKEN@*/ .title/*@END_MENU_TOKEN@*/).foregroundColor(.white).padding()
            }).background(.pink).cornerRadius(10)
            Button(action: {
                jsonToStr()
            }, label: {
                Text("编码JSON").font(/*@START_MENU_TOKEN@*/ .title/*@END_MENU_TOKEN@*/).foregroundColor(.white).padding()
            }).background(.blue).cornerRadius(10)
            Spacer()
            Spacer()
            Spacer()
        }
    }
}

 

拓展知识点

1.可选属性类型

如果你自定义的类型有一个逻辑上允许值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时机可以赋值为空——都需要将它声明为 可选类型。可选类型的属性将自动初始化为 nil,表示这个属性是特意在构造过程设置为空。

下面例子中定义了类 SurveyQuestion,它包含一个可选 String 属性 response

class SurveyQuestion {
    var text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}

let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// 打印“Do you like cheese?”
cheeseQuestion.response = "Yes, I do like cheese."

调查问题的答案在询问前是无法确定的,因此我们将属性 response 声明为 String? 类型,或者说是 “可选类型 String“。当 SurveyQuestion 的实例初始化时,它将自动赋值为 nil,表明“暂时还没有字符“。

2.构造过程中常量属性的赋值

你可以在构造过程中的任意时间点给常量属性赋值,只要在构造过程结束时它设置成确定的值。一旦常量属性被赋值,它将永远不可更改。

注意

对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。

你可以修改上面的 SurveyQuestion 示例,用常量属性替代变量属性 text,表示问题内容 textSurveyQuestion 的实例被创建之后不会再被修改。尽管 text 属性现在是常量,我们仍然可以在类的构造器中设置它的值:

class SurveyQuestion {
    let text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// 打印“How about beets?”
beetsQuestion.response = "I also like beets. (But not with cheese.)"

3.默认构造器

如果结构体或类为所有属性提供了默认值,又没有提供任何自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器。这个默认构造器将简单地创建一个所有属性值都设置为它们默认值的实例。

下面例子中定义了一个类 ShoppingListItem,它封装了购物清单中的某一物品的名字(name)、数量(quantity)和购买状态 purchase state

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()

由于 ShoppingListItem 类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个将为所有属性设置默认值的并创建实例的默认构造器(由于 name 属性是可选 String 类型,它将接收一个默认 nil 的默认值,尽管代码中没有写出这个值)。上面例子中使用默认构造器创造了一个 ShoppingListItem 类的实例(使用 ShoppingListItem() 形式的构造器语法),并将其赋值给变量 item

可选链

可选链式调用是一种可以在当前值可能为 nil 的可选值上请求和调用属性、方法及下标的方法。如果可选值有值,那么调用就会成功;如果可选值是 nil,那么调用将返回 nil。多个调用可以连接在一起形成一个调用链,如果其中任何一个节点为 nil,整个调用链都会失败,即返回 nil

使用可选链式调用代替强制解包

通过在想调用的属性、方法,或下标的可选值后面放一个问号(?),可以定义一个可选链。这一点很像在可选值后面放一个叹号(!)来强制解包它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制解包将会触发运行时错误。

为了反映可选链式调用可以在空值(nil)上调用的事实,不论这个调用的属性、方法及下标返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来判断你的可选链式调用是否调用成功,如果调用有返回值则说明调用成功,返回 nil 则说明调用失败。

这里需要特别指出,可选链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可选值。例如,使用可选链式调用访问属性,当可选链式调用成功时,如果属性原本的返回结果是 Int 类型,则会变为 Int? 类型。

下面几段代码将解释可选链式调用和强制解包的不同。

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

Residence 有一个 Int 类型的属性 numberOfRooms,其默认值为 1Person 具有一个可选的 residence 属性,其类型为 Residence?

假如你创建了一个新的 Person 实例,它的 residence 属性由于是可选类型而将被初始化为 nil,在下面的代码中,john 有一个值为 nilresidence 属性:

复制

let john = Person()

如果使用叹号(!)强制解包获得这个 johnresidence 属性中的 numberOfRooms 值,会触发运行时错误,因为这时 residence 没有可以解包的值:

复制

let roomCount = john.residence!.numberOfRooms
// 这会引发运行时错误

john.residence 为非 nil 值的时候,上面的调用会成功,并且把 roomCount 设置为 Int 类型的房间数量。正如上面提到的,当 residencenil 的时候,上面这段代码会触发运行时错误。

可选链式调用提供了另一种访问 numberOfRooms 的方式,使用问号(?)来替代原来的叹号(!):

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// 打印“Unable to retrieve the number of rooms.”

residence 后面添加问号之后,Swift 就会在 residence 不为 nil 的情况下访问 numberOfRooms

因为访问 numberOfRooms 有可能失败,可选链式调用会返回 Int? 类型,或称为“可选的 Int”。如上例所示,当 residencenil 的时候,可选的 Int 将会为 nil,表明无法访问 numberOfRooms。访问成功时,可选的 Int 值会通过可选绑定解包,并赋值给非可选类型的 roomCount 常量。

要注意的是,即使 numberOfRooms 是非可选的 Int 时,这一点也成立。只要使用可选链式调用就意味着 numberOfRooms 会返回一个 Int? 而不是 Int

可以将一个 Residence 的实例赋给 john.residence,这样它就不再是 nil 了:

john.residence = Residence()

john.residence 现在包含一个实际的 Residence 实例,而不再是 nil。如果你试图使用先前的可选链式调用访问 numberOfRooms,它现在将返回值为 1Int? 类型的值:

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// 打印“John's residence has 1 room(s).”

更多详细的资料可以查看:可选链 | SwiftGG 

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

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

相关文章

C++第二十弹---深入理解STL中vector的使用

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1、vector的介绍 2、vector的使用 2.1、构造函数和赋值重载 2.1.1、构造函数的介绍 2.1.2、代码演示 2.2、容量操作 2.3、遍历 2.4、增删…

类和对象(上)【有关类的全面学习】【this指针的学习】

类和对象(上) 1.面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C语言注重过程: C是基于面向对象的,关注的是对象&#xff0…

如何获取某个城市或区域的人口分布数据?

人口分布数据在多个领域都扮演着至关重要的角色。这些数据不仅反映了一个国家或地区的人口分布状况,而且为政策制定者、企业决策者和研究者提供了宝贵的信息。那么,我们如何获取这些重要的人口分布数据呢? 政府统计部门是最主要的来源。各国政…

绝缘鞋计量校准周期多长时间合适?校验检测方法是什么?

绝缘鞋的计量校准,通常是应用在电学相关领域,因此也是属于计量校准机构中的电学室管辖的范围,而绝缘鞋为了安全防护,也是采用了绝缘材料,其标准要求耐电压至少在15KV以下,可应用于工频(50到60F&…

git 检查用户是否是gitlab用户

背景: 公司代码要从老的git库迁到新的git库,老git库上部分提交用户在新git库上没有,解决方法: 让gitlab不再检查提交用户是否是gitlab用户。具体操作: 去掉下面的勾选,保存配置即可。

天气的雪碧图标(晴天,雨天,雪天,阴天,雾天,多云等)(2024-05-27)

天气的预览图标,可以自行下载,或者在资源中下载高清的

Matlab进阶绘图第57期—带填充纹理的横向柱状图

带填充纹理的横向柱状图是通过在原始横向柱状图的基础上添加不同的纹理得到的,可以很好地解决由于颜色区分不足而导致的对象识别困难问题。 由于Matlab中未提供纹理填充选项,因此需要大家自行设法解决。 本文使用Kesh Ikuma制作的hatchfill2工具&#…

buu[HCTF 2018]WarmUp(代码审计)

buu[HCTF 2018]WarmUp&#xff08;代码审计&#xff09; 题目 访问source.php <?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist ["source">"source.php","hint">"hint.php…

屎山代码SSM转换Springboot

SSM项目转Springboot项目 最近很多人可能是在网上买的那种屎山代码&#xff0c;数据库都是拼音的那种 比如项目如下所示&#xff1a; 这种屎山代码我改过太多了&#xff0c;很多人可能无从下手&#xff0c;因为代码结构太混乱了&#xff0c;但是我改过太多这种代码&#xff0…

【SpeedAI科研小助手】2分钟解决知网维普AIGC检测

2分钟搞定AIGC率&#xff1f;还能降到0%&#xff1f; 使用方法&#xff1a; 打开SpeedAI科研小助手&#xff0c;将功能模式换成降AIGC率&#xff0c;后面可以一段一段自己改&#xff0c;也可以直接上传论文文件&#xff0c;SpeedAI直接帮你全文修改&#xff08;主打一个用户友…

【云原生】kubernetes中的认证、权限设置---RBAC授权原理分析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

笔记本电脑的ip地址在哪里找

在数字化时代&#xff0c;IP地址作为网络设备的唯一标识&#xff0c;扮演着至关重要的角色。对于笔记本电脑用户而言&#xff0c;了解如何查找自己的IP地址不仅有助于网络故障排查&#xff0c;还能在网络安全和远程办公等场景中发挥关键作用。虎观代理小二将详细介绍笔记本电脑…

关于linux磁盘告警问题

案例&#xff1a;我们在执行df命令时&#xff0c;查看到磁盘利用率很高&#xff0c;但是到相对应的目录执行du -sh *来找大文件时进行删除时&#xff0c;发现各个目录相加并不大&#xff0c;如下图&#xff1a; 使用df命令查看到根(/)目录使用到33G&#xff0c;而du命令显示只使…

【一竞技DOTA2】北美SR战队官宣SabeRLighT-离队

1、近日北美SR战队官宣了旗下三号位选手SabeRLighT-正式离队&#xff0c;公告内容如下&#xff1a; “今日我们正式宣布&#xff0c;Jonas &#xff08;Saberlight&#xff09;Volek将离开队伍。这个决定由Jonas本人和其他团队成员共同做出。 在过去的几个月里&#xff0c;我们…

mysql中InnoDB的表空间--独立表空间

大家好&#xff0c;上篇文章我们在讲mysql数据目录的时候提到了表空间这个名词&#xff0c;它是一个抽象的概念&#xff0c;对于系统表空间来说&#xff0c;对应着文件系统中一个或多个实际文件&#xff1b;对于每个独立表空间来说&#xff0c;对应着文件系统中一个名为表名.ib…

中医理疗元宇宙 科技赋能中医药产业走向国际市场

基于380亿参数量&#xff0c;对中医药海量文本进行数据训练&#xff0c;实现方剂优化、机制阐释和新适应症的精准发现……日前在天津召开的数智赋能大健康产业新质生产力暨第四届中医药国际发展大会上&#xff0c;由天士力医药集团与华为云共同开发的“数智本草”中医药大模型正…

QT C++ QTableWidget+combobox 槽函数 演示

本文演示了 QTableWidget的初始化以及单元格值改变时响应槽函数&#xff0c;打印单元格。 并且&#xff0c;最后列不一样,是组合框(combobox) &#xff0c;此列的槽函数用lambda函数。 在QT6.2.4 MSVC2019 调试通过。 1.界面效果 2.头文件 #ifndef MAINWINDOW_H #define MA…

关于Nginx热部署的细节分析

文章目录 前言一、环境准备二、热部署步骤总结 前言 Nginx由于其高并发、高性能、可扩展性好、高可靠性、热部署、BSD许可证等优势被广泛使用&#xff0c;本人主要针对热部署的部分展开说明热部署的具体步骤以及步骤背后发生的具体事情。 本次热部署采用的Nginx版本号为&…

【Web】2024京麒CTF ezjvav题解

目录 step 0 step 1 step 2 EXP1 EXP2 step 0 进来是一个登录框 admin/admin成功登录 访问./source jwt伪造 带着伪造的jwt访问./source&#xff0c;拿到题目源码jar包 step 1 pom依赖有spring、fj、rome 反序列化入口在./Jsrc路由 有两层waf&#xff0c;一个是明…