在Swift 4推出Codable之后,我們基本上可以拋棄字典轉(zhuǎn)模型的第三方庫(kù)了儒陨。在我自己的使用過(guò)程中,發(fā)現(xiàn)了一些會(huì)導(dǎo)致無(wú)法解碼JSON的細(xì)節(jié)問(wèn)題笋籽。在此跟大家分享下蹦漠。
一、類型的某個(gè)屬性有默認(rèn)值车海,后臺(tái)返回的JSON沒(méi)有這個(gè)屬性對(duì)應(yīng)的數(shù)據(jù)
正常的Demo
User
假設(shè)我們有一個(gè)User
類型笛园,有一個(gè)id
屬性,和一個(gè)是否被當(dāng)前用戶關(guān)注的屬性isFollowedByCurrentUser
容劳,并實(shí)現(xiàn)了Codable協(xié)議喘沿,代碼如下:
struct User: Codable {
var id: String
var isFollowedByCurrentUser: Bool?
enum CodingKeys: String, CodingKey {
case id
case isFollowedByCurrentUser = "followed"
}
}
解碼
我們的JSON數(shù)據(jù)如下:
let jsonString = """
{
"id":"efa41bae-25fa-428b-99c1-6d3c1b178875",
"followed": true
}
"""
用JSONDecoder
進(jìn)行解碼:
let decoder = JSONDecoder()
let data = jsonString.data(using: .utf8)!
do {
let user = try decoder.decode(User.self, from: data)
print(user)
} catch {
print("error: \(error)")
}
毫無(wú)疑問(wèn),上面的代碼是可以解碼成功的竭贩。
失敗的Demo
有些時(shí)候蚜印,后臺(tái)返回的JSON數(shù)據(jù)可能缺少某些字段,假設(shè)缺少了followed
留量,那么現(xiàn)在的JSON數(shù)據(jù)為:
let jsonString = """
{
"id":"efa41bae-25fa-428b-99c1-6d3c1b178875"
}
"""
這時(shí)我們用上面的JSONDecoder
進(jìn)行解碼窄赋,也是可以解碼成功的,只不過(guò)isFollowedByCurrentUser
的值為nil
而已楼熄。
現(xiàn)在問(wèn)題來(lái)了忆绰,我們看回User
類型。通常我們?cè)谀硞€(gè)類型添加一個(gè)Bool
屬性時(shí)可岂,一般會(huì)給他一個(gè)默認(rèn)值false
错敢,所以我們會(huì)習(xí)慣的把User
寫成:
struct User: Codable {
var id: String
var isFollowedByCurrentUser = false
enum CodingKeys: String, CodingKey {
case id
case isFollowedByCurrentUser = "followed"
}
}
這時(shí)如果我們?cè)儆?code>JSONDecoder把缺少followed
字段的JSON數(shù)據(jù)轉(zhuǎn)成User
的話,是無(wú)法轉(zhuǎn)成功的缕粹,錯(cuò)誤如下:
error: keyNotFound(CodingKeys(stringValue: "followed", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"followed\", intValue: nil) (\"followed\").", underlyingError: nil))
JSONDecoder
在JSON數(shù)據(jù)中無(wú)法找到followed
對(duì)應(yīng)的值稚茅。
解決辦法
我們無(wú)法保證服務(wù)器總是返回完整的數(shù)據(jù),所以只能從我們客戶端去解決問(wèn)題平斩。
1. 把類型的所有屬性都定義為Optional類型
這是最簡(jiǎn)單方便的方法亚享。這樣解碼的時(shí)候,JSONDecoder
發(fā)現(xiàn)JSON沒(méi)有對(duì)應(yīng)的數(shù)據(jù)绘面,就自動(dòng)把這個(gè)屬性設(shè)置為nil
欺税。
2. 實(shí)現(xiàn)Decodable的初始化函數(shù)侈沪,并使用decodeIfPresent
來(lái)解碼
正常情況下,我們定義了CodingKeys
之后晚凿,不需要手動(dòng)實(shí)現(xiàn)init(from decoder: Decoder) throws
這個(gè)初始化函數(shù)的亭罪,JSONDecoder
就可以正常解碼。但是我們把isFollowedByCurrentUser
定義成一個(gè)非可選類型晃虫,我們必須實(shí)現(xiàn)這個(gè)初始化函數(shù)皆撩,才能正常解碼:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
isFollowedByCurrentUser = try container.decodeIfPresent(Bool.self, forKey: .isFollowedByCurrentUser) ?? false
}
完
歡迎加入我管理的Swift開(kāi)發(fā)群:536353151
扣墩。