[英]Decoding json with optional fields
我正在嘗試解析一個 json 字符串,其中一些鍵未固定。 有一些與錯誤相關的鍵,要么是錯誤,要么是結果數據。 以下是兩個例子:
{
"ok": true,
"result": {
"code": "694kyH",
"short_link": "shrtco.de\/694kyH",
"full_short_link": "https:\/\/shrtco.de\/694kyH",
"short_link2": "9qr.de\/694kyH",
"full_short_link2": "https:\/\/9qr.de\/694kyH",
"short_link3": "shiny.link\/694kyH",
"full_short_link3": "https:\/\/shiny.link\/694kyH",
"share_link": "shrtco.de\/share\/694kyH",
"full_share_link": "https:\/\/shrtco.de\/share\/694kyH",
"original_link": "http:\/\/google.com"
}
}
{
"ok": false,
"error_code": 2,
"error": "This is not a valid URL, for more infos see shrtco.de\/docs"
}
我將如何解析這個 JSON。我嘗試像下面這樣構建我的 class 但它不起作用:
struct ShortLinkData: Codable {
let ok: Bool
let result: Result?
let errorCode: Int?
let error: String?
private enum CodingKeys : String, CodingKey { case ok, result, errorCode = "error_code", error }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
ok = try container.decode(Bool.self, forKey: .ok)
result = try container.decode(Result.self, forKey: .result)
errorCode = try container.decodeIfPresent(Int.self, forKey: .errorCode)
error = try container.decodeIfPresent(String.self, forKey: .error)
}
}
// MARK: - Result
struct Result: Codable {
let code, shortLink: String
let fullShortLink: String
let shortLink2: String
let fullShortLink2: String
let shortLink3: String
let fullShortLink3: String
let shareLink: String
let fullShareLink: String
let originalLink: String
enum CodingKeys: String, CodingKey {
case code
case shortLink = "short_link"
case fullShortLink = "full_short_link"
case shortLink2 = "short_link2"
case fullShortLink2 = "full_short_link2"
case shortLink3 = "short_link3"
case fullShortLink3 = "full_short_link3"
case shareLink = "share_link"
case fullShareLink = "full_share_link"
case originalLink = "original_link"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
code = try container.decode(String.self, forKey: .code)
shortLink = try container.decode(String.self, forKey: .shortLink)
fullShortLink = try container.decode(String.self, forKey: .fullShortLink)
shortLink2 = try container.decode(String.self, forKey: .shortLink2)
fullShortLink2 = try container.decode(String.self, forKey: .fullShortLink2)
shortLink3 = try container.decode(String.self, forKey: .shortLink3)
fullShortLink3 = try container.decode(String.self, forKey: .fullShortLink3)
shareLink = try container.decode(String.self, forKey: .shareLink)
fullShareLink = try container.decode(String.self, forKey: .fullShareLink)
originalLink = try container.decode(String.self, forKey: .originalLink)
}
}
我的解析代碼:
let str = String(decoding: data, as: UTF8.self)
print(str)
let shortURL = try? JSONDecoder().decode(ShortLinkData.self, from: data)
return shortURL!
我總是在 shortURL object 中得到 nil。
你應該把它分成幾個步驟,以避免在你的 model 中處理所有這些選項。
首先創建一個結構,它只包含那些保證存在的屬性。 ok
,在你的情況下:
struct OKResult: Codable{
let ok: Bool
}
然后為您的錯誤創建一個 state,為您的成功創建一個 state:
struct ErrorResult: Codable{
let ok: Bool
let errorCode: Int
let error: String
private enum CodingKeys: String, CodingKey{
case ok, errorCode = "error_code", error
}
}
struct ShortLinkData: Codable {
let ok: Bool
let result: Result
}
struct Result: Codable {
let code, shortLink: String
let fullShortLink: String
let shortLink2: String
let fullShortLink2: String
let shortLink3: String
let fullShortLink3: String
let shareLink: String
let fullShareLink: String
let originalLink: String
enum CodingKeys: String, CodingKey {
case code
case shortLink = "short_link"
case fullShortLink = "full_short_link"
case shortLink2 = "short_link2"
case fullShortLink2 = "full_short_link2"
case shortLink3 = "short_link3"
case fullShortLink3 = "full_short_link3"
case shareLink = "share_link"
case fullShareLink = "full_share_link"
case originalLink = "original_link"
}
}
然后你可以解碼數據:
guard try JSONDecoder().decode(OKResult.self, from: data).ok else{
let errorResponse = try JSONDecoder().decode(ErrorResult.self, from: data)
//handle error scenario
fatalError(errorResponse.error) // or throw custom error or return nil etc...
}
let shortlinkData = try JSONDecoder().decode(ShortLinkData.self, from: data)
評論:
init
不是必需的。try?
這將對您隱藏所有錯誤do catch
塊中,或者讓您的 function throwing
錯誤並在樹上進一步處理錯誤。實際上沒有可選字段。 服務器發送兩個不同但不同的 JSON 字符串。
解碼兩個 JSON 字符串的合適方法是使用具有關聯值的枚舉。 它解碼ok
鍵,然后解碼result
字典或errorCode
和error
enum Response : Decodable {
case success(ShortLinkData), failure(Int, String)
private enum CodingKeys : String, CodingKey { case ok, result, errorCode, error }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let ok = try container.decode(Bool.self, forKey: .ok)
if ok {
let result = try container.decode(ShortLinkData.self, forKey: .result)
self = .success(result)
} else {
let errorCode = try container.decode(Int.self, forKey: .errorCode)
let error = try container.decode(String.self, forKey: .error)
self = .failure(errorCode, error)
}
}
}
在ShortLinkData
中,如果您指定convertFromSnakeCase
密鑰解碼策略,則init
方法和CodingKeys
是多余的
struct ShortLinkData: Decodable {
let code, shortLink: String
let fullShortLink: String
let shortLink2, fullShortLink2: String
let shortLink3, fullShortLink3: String
let shareLink, fullShareLink: String
let originalLink: String
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Response.self, from: data)
switch result {
case .success(let linkData): print(linkData)
case .failure(let code, let message): print("An error occurred with code \(code) and message \(message)")
}
} catch {
print(error)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.