[英]Decoding json with optional fields
I am trying to parse a json string in which some of the keys are not fixed.我正在尝试解析一个 json 字符串,其中一些键未固定。 There are some keys which are related to error and either it will be error or the result data.
有一些与错误相关的键,要么是错误,要么是结果数据。 Followings are the two examples:
以下是两个例子:
{
"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"
}
How will I parse this JSON. I have tried to build my class like following but it is not working:我将如何解析这个 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)
}
}
My parsing code:我的解析代码:
let str = String(decoding: data, as: UTF8.self)
print(str)
let shortURL = try? JSONDecoder().decode(ShortLinkData.self, from: data)
return shortURL!
I am always getting nil in shortURL object.我总是在 shortURL object 中得到 nil。
You should split this into several steps in order to avoid to handle all these optionals in your model.你应该把它分成几个步骤,以避免在你的 model 中处理所有这些选项。
First create a struct that has only those properties that are guaranteed to be there.首先创建一个结构,它只包含那些保证存在的属性。
ok
in your case: ok
,在你的情况下:
struct OKResult: Codable{
let ok: Bool
}
then create one for your error state and one for your success state:然后为您的错误创建一个 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"
}
}
Then you can decode the data:然后你可以解码数据:
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)
Remarks:评论:
init
s are not necessary.init
不是必需的。try?
try?
this will hide all errors from youdo catch
block or make your function throwing
and handle errors further up the tree.do catch
块中,或者让您的 function throwing
错误并在树上进一步处理错误。 Actually there are no optional fields .实际上没有可选字段。 The server sends two different but distinct JSON strings.
服务器发送两个不同但不同的 JSON 字符串。
A suitable way to decode both JSON strings is an enum with associated values.解码两个 JSON 字符串的合适方法是使用具有关联值的枚举。 It decodes the
ok
key, then it decodes either the result
dictionary or errorCode
and error
它解码
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)
}
}
}
In ShortLinkData
the init
method and the CodingKeys
are redundant if you specify the convertFromSnakeCase
key decoding strategy在
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.