Json response from this call https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/usd/eur.min.json is pretty basic
{
"date": "2022-12-27",
"eur": 0.939751
}
first property is always named "date" and it's always String second is Double but it's name/key is dynamic, it can be "usd", "eur" etc.
I have tried
struct RateResponse: Decodable {
let date: String
let rate: Double
enum CodingKeys: CodingKey {
case date
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
date = try container.decode(String.self, forKey: CodingKeys.date)
let singleValueContainer = try decoder.singleValueContainer()
rate = try singleValueContainer.decode(Double.self)
}
}
got this "Expected to decode Double but found a dictionary instead." I know what error says but not to sure how to solve it.
CodePudding user response:
My suggestion is a custom KeyDecodingStrategy
.
First you need a neutral CodingKey
struct to replace the currency key.
struct AnyKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) { self.stringValue = stringValue }
init?(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
The RateResponse
struct can be reduced to
struct RateResponse: Decodable {
let date: String
let rate: Double
}
The key decoding strategy passes the date
key and replaces anything else with rate
let jsonString = """
{
"date": "2022-12-27",
"eur": 0.939751
}
"""
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({
let value = $0.last!.stringValue
switch value {
case "date": return $0.last!
default: return AnyKey(stringValue: "rate")!
}
})
let result = try decoder.decode(RateResponse.self, from: Data(jsonString.utf8))
print(result)
} catch {
print(error)
}