I have an API that will sometimes return a specific key value (in this case id
) in the JSON as an Int and other times it will return that same key value as a String. How do I use codable to parse that JSON?
struct GeneralProduct: Codable { var price: Double! var id: String? var name: String! private enum CodingKeys: String, CodingKey { case price = "p" case id = "i" case name = "n" } init(price: Double? = nil, id: String? = nil, name: String? = nil) { self.price = price self.id = id self.name = name } }
I keep getting this error message: Expected to decode String but found a number instead
. The reason that it returns a number is because the id field is empty and when the id field is empty it defaults to returning 0 as an ID which codable identifies as a number. I can basically ignore the ID key but codable does not give me the option to ignore it to my knowledge. What would be the best way to handle this?
Here is the JSON. It is super simple
Working
{ "p":2.12, "i":"3k3mkfnk3", "n":"Blue Shirt" }
Error - because there is no id in the system, it returns 0 as a default which codable obviously sees as a number opposed to string.
{ "p":2.19, "i":0, "n":"Black Shirt" }
Remember, Swift's String , Int , and Bool are all Codable ! Earlier I wrote that your structs, enums, and classes can conform to Codable . Swift can generate the code needed to extract data to populate a struct's properties from JSON data as long as all properties conform to Codable .
If all the properties of a type already conform to Codable , then the type itself can conform to Codable with no extra work – Swift will synthesize the code required to archive and unarchive your type as needed.
A type that can be used as a key for encoding and decoding. A type that can be converted to and from a coding key. A user-defined key for providing context during encoding and decoding.
typealias Codable = Decodable & Encodable. Decodable protocol Decodable : A type that can decode itself from an external representation (bytes to object) Encodable protocol Encodable : A type that can encode itself to an external representation. ( object to bytes) Codable = both encoding and decoding.
struct GeneralProduct: Codable { var price: Double? var id: String? var name: String? private enum CodingKeys: String, CodingKey { case price = "p", id = "i", name = "n" } init(price: Double? = nil, id: String? = nil, name: String? = nil) { self.price = price self.id = id self.name = name } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) price = try container.decode(Double.self, forKey: .price) name = try container.decode(String.self, forKey: .name) do { id = try String(container.decode(Int.self, forKey: .id)) } catch DecodingError.typeMismatch { id = try container.decode(String.self, forKey: .id) } } }
let json1 = """ { "p":2.12, "i":"3k3mkfnk3", "n":"Blue Shirt" } """ let json2 = """ { "p":2.12, "i":0, "n":"Blue Shirt" } """
do { let product = try JSONDecoder().decode(GeneralProduct.self, from: Data(json2.utf8)) print(product.price ?? "nil") print(product.id ?? "nil") print(product.name ?? "nil") } catch { print(error) }
edit/update:
You can also simply assign nil
to your id
when your api returns 0
:
do { let value = try container.decode(Int.self, forKey: .id) id = value == 0 ? nil : String(value) } catch DecodingError.typeMismatch { id = try container.decode(String.self, forKey: .id) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With