如何优雅的使用Swift Codable协议

在Swift开发中,JSON数据序列化是一个避不开的工作,Swift由于类型安全的特性,对于像JSON这类弱类型的数据处理一直是一个比较头疼的问题,Swift 4 带来的新特性中, Codable 协议让人眼前一亮。但是, Codable也不能完全满足我们的要求,比如不支持类型的自动转换、对默认值支持不友好。 so,我们如果把这些问题解决了,是不是就完美啦

Codable坑点1:不支持类型转换

// JSON:
{
    "uid":"123456",
    "name":"Harry",
    "age":10
}

// Model:
struct Dog: Codable{
    var uid: Int
    var name: String?
    var age: Int?
}
复制代码

在json转换过程中,我们常常与遇到类型模型与json的类型不一致的情况,就像上面的uid字段,uid在json中是String,但是我们的模型是Int,由于swift是类型安全的,所以,转换就不会成功。

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:834688868,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

Codable坑点2:不支持默认值

话不多说,上代码

struct Activity: Codable {
    enum Status: Int {
        case start = 1//活动开始
        case processing = 2//活动进行中
        case end = 3//活动结束
    }

    var name: String
    var status: Status//活动状态
}
复制代码

这儿有一个活动,活动现目前有三种状态,到目前为止,一切都很美好。有一天,突然说需要给活动添加已下架的状态,what?

//JSON
{
    "name": "元旦迎新活动",
    "status": 4
}
复制代码

用Activity解析上面的JSON就会报错,我们如何规避呢,像下面一样

var status: Status?
复制代码

答案是no、no、no,因为可选值的解码所表达的是“如果不存在,则置为 nil”,而不是“如果解码失败,则置为 nil”。

解决方案

有没有更好的方式来处理上面这两个问题呢?答案是使用 property wrapper。具体代码见ObjMapper,这儿简单描述下如何使用。

Model与JSON相互转换

// JSON:
{
    "uid":888888,
    "name":"Tom",
    "age":10
}

// Model:
struct Dog: Codable{
    //如果字段不是可选类型,则使用Default,提供一个默认值,像下面一样
    @Default<Int.Zero> var uid: Int
    //如果是可选类型,则使用Backed
    @Backed var name: String?
    @Backed var age: Int?
}

//JSON to model
let dog = Dog.decodeJSON(from: json)

//model to json
let json = dog.jsonString
复制代码

当 JSON/Dictionary 中的对象类型与 Model 属性不一致时,ObjMapper 将会进行如下自动转换。自动转换不支持的值将会被设置为nil或者默认值。

JSON/Dictionary Model
String String,Number类型(包含整数,浮点数),Bool
Number类型(包含整数,浮点数) Number类型,String,Bool
Bool Bool,String,Number类型(包含整数,浮点数)
nil nil,0

Model的嵌套

{
  "author": {
    "id": 888888,
    "name": "Alex",
    "age": "10"
  },
  "title": "model与json互转",
  "subTitle": "如何优雅的转换"
}

// Model:
struct Author: Codable{
    @Default<Int.Zero> var uid: Int
    @Default<String.Empty> var name: String
    //使用Backed后,如果类型不匹配,则会类型转换
    @Backed var age: Int?
}

struct Article: Codable {
    //如果json中的title为nil或者不存在,则会给title赋一个默认值
    @Default<String.Empty> var title: String
    var subTitle: String?
    var author: Author
}

//JSON to model
let article = Article.decodeJSON(from: json)

//model to json
let json = article.jsonString
复制代码

类型的默认值

struct Activity: Codable {
    ///Step 1:让Status遵循DefaultValue协议
    enum Status: Int, Codable, DefaultValue {
        case start = 1//活动开始
        case processing = 2//活动进行中
        case end = 3//活动结束
        case unknown = 0//默认值,无意义

        ///Step 2:实现DefaultValue协议,指定一个默认
        static let defaultValue = Status.unknown
    }

    @Default<String.Empty> var name: String
    ///Step 3:使用Default
    @Default<Status> var status: Status//活动状态
}

///对可选类型值的支持
let json = """
{
    "name": "元旦迎新活动",
    "status": 4
}
"""
///可选类型自动转换
let activity = Activity.decodeJSON(from: json)!
///activity的status,转换为unknown
print("activity.status: \(activity.status)")
///
print("json:\(activity.jsonString ?? "")")
复制代码

为普通类型设置不一样的默认值

ObjMapper已经内置了很多默认值,比如Int.Zero, Bool.True, String.Empty...,如果我们想为字段设置不一样的默认值,见下面代码:

public extension Int {
    enum One: DefaultValue {
        public static let defaultValue = 1
    }
}

struct Dog: Codable{
    @Backed var name: String?
    @Default<Int.Zero> var uid: Int
    //如果json中没有age字段或者解析失败,则模型的age被设置成默认值1
    @Default<Int.One> var age: Int
}
复制代码

参考文档

  1. 用 Codable 协议实现快速 JSON 解析
  2. Swift 4 踩坑之 Codable 协议
  3. 使用 Property Wrapper 为 Codable 解码设定默认值

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:834688868,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

作者:大儿童梦里花开
链接:https://juejin.cn/post/6910094553684901895
来源:掘金

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容