У меня есть ответ API JSON, как показано ниже. Я хочу декодировать JSON, чтобы получить массив словарей [String:Double]
, например [{"2020-01-01" : 0.891186}, {"2020-01-02" : 0.891186}]
.
{
"rates": {
"2020-01-01": {
"EUR": 0.891186
},
"2020-01-02": {
"EUR": 0.891186
},
"2020-01-03": {
"EUR": 0.895175
},
"2020-01-04": {
"EUR": 0.895175
}
}
}
Я написал код декодирования, как показано ниже:
do {
let data = try Data(contentsOf: appURL)
let decoder = JSONDecoder()
let response = try decoder.decode(Rates.self, from: data)
response.rates
} catch let jsonError {
print(jsonError)
}
И я попытался определить структуру:
struct Rates: Codable, Hashable {
let rates: Point
}
struct Point {
}
Но я понятия не имею, что я должен писать в struct Point
, потому что дата не является постоянным полем.
Вот два возможных решения: одно со структурой Point
, а другое со словарем.
Сначала решение с Point
struct Point: Codable {
let date: String
let rate: Double
}
А затем создайте собственный init(from:)
, где мы сначала декодируем json в словарь, [String: [String: Double]]
а затем сопоставляем этот словарь с массивом Point
struct Rates: Codable {
let rates: [Point]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let dictionary = try container.decode([String: [String: Double]].self, forKey: .rates)
rates = dictionary.map { Point(date: $0.key, rate: $0.value.first?.value ?? .zero) }
}
}
А вот второе решение с использованием словаря
struct Rates: Codable {
let rates: [String: Double]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let dictionary = try container.decode([String: [String: Double]].self, forKey: .rates)
rates = dictionary.compactMapValues { $0.first?.value }
}
}
Я использовал опубликованный вами json и тот же код для декодирования, что и вы, за исключением создания переменной data
без каких-либо проблем, поэтому является ли json правильным, потому что я не вижу в нем свойства даты?
Теперь все хорошо! Спасибо за помощь!
struct Rates: Codable {
let rates: [String: Point]
}
// MARK: - Point
struct Point: Codable {
let eur: Double
enum CodingKeys: String, CodingKey {
case eur = "EUR"
}
}
вы можете сделать что-то вроде этого
Спасибо за повтор! Евро - это динамическое имя, а не постоянное поле, но все же ценю вашу работу!
Спасибо за повтор! Но все два метода вызывают ошибку keyNotFound, например: keyNotFound(CodingKeys(stringValue: "date", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "rates", intValue: nil), _JSONKey( stringValue: "2020-01-03", intValue: nil)], debugDescription: "Нет значения, связанного с ключом CodingKeys (stringValue: \"date\", intValue: nil) (\"date\").", baseError: ноль))