前言
JSON转模型,在Object-c中是比较常见,Swift中当然也有,比较常见的一些如下:
ObjectMapper: 面向协议的 Swift JSON 解析框架
HandyJSON: 阿里推出的一个用于 Swift 的 JSON 序列化/反序列化库。
KakaJSON: mj 新写的一个Swift的JSON转模型工具
其中上面的库基本上都是使用 Mirror反射来获取类的信息,以及赋值等(要研究一些源码才能理解赋值过程,,其实要仔细看也不难,可以直接接收大佬们的研究成果🤣,我后面也要看看记录一下),另外纯 Swift的对象已经已经不具备 runtime特性了,只能使用反射
JSONDecode: 为苹果推出的 Swift JSON转model的工具,由于是苹果自家的,也挺好用,这边就主要介绍此库了
上面的案例各有各的好处,使用也相对简单,点进去就有介绍
而JSONDecode的好处就是不依赖三方库,且使用也很方便
JSONDecode
JSONDecoder使用使用的就是该类,下面定义一个案例JSON来一步一步演示使用它
//定义一个JSON类型也比较多,相对复杂
let baseJSON = """
{
"id": 123123123,
"name": "剪刀石头布",
"age": 29,
"state": 1,
"file_info": {
"pic": "http://123.png",
"thum_pic": "http://123_thum.png",
},
"current_department": {
"id": 1,
"name": "数字孪生开发部门",
"level": 100,
"count": 20
},
"enterprise_info": {
"id": "saiirower231oi23104004",
"name": "詹小帅",
"age": 30,
"department_id": 1,
"department": [{
"id": 1,
"name": "数字孪生开发部门",
"level": 100,
"count": 20
},{
"id": 1,
"name": "小程序开发部门",
"level": 100,
"count": 20
},{
"id": 1,
"name": "APP基础开发部门",
"level": 100,
"count": 20
}]
}
}
"""
简单案例一
使用 JSONDecode 解析的模型,需要遵循 Codable 协议
如下所示,只使用了两个参数,那么就解析两个参数,遵循 Codable 协议
struct SimpleModel: Codable {
var name: String?
var age: Int = 0
}
解析案例如下所示,会自动将 json 中指定key与指定对象的参数赋值
解析过程中,失败可能会抛出异常,因此要使用 try-catch 来解决,这样就解析完毕了
let decode = JSONDecoder()
if let data = baseJSON.data(using: .utf8) {
do {
let simModel = try decode.decode(SimpleModel.self, from: data)
self.simpleJSON = simModel
print("simModel", simModel)
} catch {
print("出错了1")
}
}
下面就成功了,打印出来对象了(Optional为可选类型,即?声明的类型)
中等案例二
与简单案例不同,这次我们碰到一个特殊的键值id,在项目中我们想给他重命名,映射到对象指定属性上
那么可以使用 CodingKeys 枚举来设置映射关系
struct MiddleModel: Codable {
var ID: Int = 0
var name: String?
var age: Int = 0
//设置映射关系,一旦设置就要全设置,否则不解析指定键值
//如果与映射值一样,可以不用赋值,但是得声明出来
enum CodingKeys: String, CodingKey {
case ID = "id" //设置了之后,json中的 id 映射到当前对象的ID属性
case name = "name"
case age //如果与映射值一样,可以不用赋值,但是得声明出来
}
}
如下所示,这样就 JSON 转中等模型成功了
let decode = JSONDecoder()
if let data = baseJSON.data(using: .utf8) {
do {
let midModel = try decode.decode(MiddleModel.self, from: data)
self.middleJSON = midModel
print("middleModel", midModel)
} catch {
print("出错了2")
}
}
打印结果如下所示 ,多了个 ID
正常使用中,我们可能会想把 JSON 子对象的值,直接赋值到模型的外层属性上,其他三方转模型可能会有这功能,而这里没有,因此不可以像下面似的解析,需要按照 JOSN 对应结构来转化才行,参考复杂案例解析
//下面是一个 JSONDecode 不支持的案例
struct MiddleModel: Codable {
//var pic: String? = nil
//var thumPic: String? = nil
//注意:如上所示,无法将对象内的数据解析映射到对象外面
//只能按照正常结构解析,可以选择不解析,解析见后面的complex
enum CodingKeys: String, CodingKey {
//case pic = "file_info.pic"
//case thumPic = "file_info.thum_pic"
}
}
注意: 映射关系一旦设置,就要全部写上,否则不映射
复杂案例三
如下所示,想用到 JSON 的子对象属性,需要将子对象全部解析出来,因此,子对象也要全部声明,且需遵循 Codeabl 协议
使用过程中,如果碰到需要映射的,则此类需要映射全部属性,否则不解析
子属性则根据其自身情况,来是是否需要映射(键值与属性名一致也不需要映射)
//一旦设置了映射,则无法使用 JSONDecode的蛇形转驼峰
//子对象碰到蛇形转模型时,也需要给出映射
struct ComplexModel: Codable {
var ID: Int = 0 //实际可以用id,这里只是案例
var name: String?
var age: Int = 0
var state: State = .normal
var fileInfo: FileInfo?
var currentDepartment: Department?
var enterpriseInfo: EnterpriseInfo?
//通过对键值设置映射值,一旦设置就要全部设置,否则不解析
enum CodingKeys: String, CodingKey {
case ID = "id"
case name //名字一样可以省略后面
case age
case state
case fileInfo = "file_info"
case currentDepartment = "current_department"
case enterpriseInfo = "enterprise_info"
}
enum State: Int, Codable {
case normal = 0
case isEnter = 1
case isLeave = 2
}
//定义 file_info 指定对象
struct FileInfo: Codable {
var pic: String?
var thumPic: String?
enum CodingKeys: String, CodingKey {
case pic
case thumPic = "thum_pic"
}
}
//定义 department 指定对象
struct Department: Codable {
var id: Int = 0
var name: String?
var level: Int = 0
var count: Int = 0
}
//定义 enterprise_info 指定对象
struct EnterpriseInfo: Codable {
var id: String?
var name: String?
var age: Int = 0
var departmentId: Int = 0
var department: [Department]?
enum CodingKeys: String, CodingKey {
case id
case name
case age
case departmentId = "department_id"
case department
}
}
}
就这样,使用和前面一样的解析手段,JSON 转复杂模型也成功了,就是对象设置时稍复杂
let decode = JSONDecoder()
if let data = baseJSON.data(using: .utf8) {
do {
let comModel = try decode.decode(ComplexModel.self, from: data)
self.complexJSON = comModel
print("complexModel", comModel)
} catch {
print("出错了3")
}
}
就这样,复杂模型也转化成功了
复杂案例之自动蛇形转驼峰
看上面的转模型感觉有些复杂,实际使用,可能后台的字段拿来就直接用了,只是可能需要蛇形转驼峰
蛇形命名法:用 _ 作为间隔的键值,例如: user_info
驼峰命名法: 用 首字母大写 作为间隔的键值,例如: userInfo
如下所示,声明一个复杂类型的驼峰命名法模型
//不使用映射时,可以将 JSONDecode 的 keyDecodingStrategy 属性设置为.convertFromSnakeCase
class ComplexAutoModel: Codable {
var id: Int = 0
var name: String?
var age: Int = 0
var state: State = .normal
var fileInfo: FileInfo?
var currentDepartment: Department?
var enterpriseInfo: EnterpriseInfo?
enum State: Int, Codable {
case normal = 0
case isEnter = 1
case isLeave = 2
}
struct FileInfo: Codable {
var pic: String?
var thumPic: String?
}
struct Department: Codable {
var id: Int = 0
var name: String?
var level: Int = 0
var count: Int = 0
}
struct EnterpriseInfo: Codable {
var id: String?
var name: String?
var age: Int = 0
var departmentId: Int = 0
var department: [Department]?
}
}
解析过程,就是额外将 decode对象的 keyDecodingStrategy参数,设置成 .convertFromSnakeCase枚举即可
let decode = JSONDecoder()
//蛇形转驼峰,模型定义不能使用映射,不然会解析失败
decode.keyDecodingStrategy = .convertFromSnakeCase
if let data = baseJSON.data(using: .utf8) {
do {
let comAutoModel = try decode.decode(ComplexAutoModel.self, from: data)
self.complexAutoJSON = comAutoModel
print("complexAutoModel", comAutoModel)
} catch {
print("出错了3")
}
}
如下所示,解析成功了(这里没打印出来,直接看的对象)
对象转JSON扩展知识
使用中,难免会碰到对象转JSON,解析JSONData等,下面给出一些案例使用
//模型 转 jsonData
let jsonData = try JSONEncoder().encode(simModel)
//jsonData 转 字典
let jsonDic = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
//jsonData 转 jsonString
let jsonString = String(data: jsonData, encoding: .utf8)
print("jsonDic", jsonDic)
if let json = jsonString {
print("jsonString", json)
}
最后
快来试一下吧,其他三方也可以试一试,没准有你喜欢的
另外推荐研究一个 Mirror反射,字典转模型的核心步骤就是这个,反射键值和写入,其他的都是在准备保存等操作
另外,祝大家新年快乐!