Reputation: 179
when trying to parse return data from API im getting "The data couldn’t be read because it isn’t in the correct format." because the return is inconsistent.
When logo_url has value it was a object see example below:
"logo_url": {
"mime_type": "image/jpeg",
"url": "http://google.com"
},
But when it doenst have value its return empty array
"logo_url": [],
This is the reason why im getting "The data couldn’t be read because it isn’t in the correct format."
My model
struct Model: Decodable {
let logo: Logo?
enum CodingKeys: String, CodingKey {
case logo = "logo_url"
}
}
struct Logo: Decodable {
let mimeType: String?
let url: String?
enum CodingKeys: String, CodingKey {
case mimeType = "mime_type"
case url
}
}
Upvotes: 0
Views: 108
Reputation: 1274
I personally prefer checking if the logo_url
is an array first, then let Swift report error if there is any happens by using try
instead of try?
when trying to decode a key. Since in most cases, you may want to know why your decoding failed instead of just getting nil
as a result.
Additionally, you may want to use .convertFromSnakeCase
as a keyDecodingStrategy
so you don't have to write extra code.
let json2 = """
{
"logo_url": {
"mime_type": "image/jpeg",
"url": "http://google.com"
}
}
"""
let json3 = "[]"
struct Logo: Decodable {
let mimeType: String
let url: String
}
struct Model: Decodable {
let logo: Logo?
private enum CodingKeys: String, CodingKey {
case logoUrl
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if (try? container.decode([String].self, forKey: .logoUrl)) != nil {
self.logo = nil
} else {
self.logo = try container.decode(Logo.self, forKey: .logoUrl)
}
}
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result2 = try decoder.decode(Model.self, from: json2.data(using: .utf8)!)
print(result2)
let result3 = try? decoder.decode(Model.self, from: json3.data(using: .utf8)!)
print(result3)
Upvotes: 1
Reputation: 49590
If you can't change this badly written API, you'd need a custom decoder, where you basically attempt to decode as the type you want, and failing that - make it nil
:
struct Model: Decodable {
let logo: Logo?
enum CodingKeys: String, CodingKey {
case logo = "logo_url"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let logo = try? container.decode(Logo.self, forKey: .logo) {
self.logo = logo
} else {
self.logo = nil
}
}
}
Upvotes: 1