Как декодировать JSON в Swift?

У меня есть ответ 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, потому что дата не является постоянным полем.

Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
0
0
81
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот два возможных решения: одно со структурой 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 }
    }
}

Спасибо за повтор! Но все два метода вызывают ошибку 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: ноль))

Arcgo 20.11.2022 08:02

Я использовал опубликованный вами json и тот же код для декодирования, что и вы, за исключением создания переменной data без каких-либо проблем, поэтому является ли json правильным, потому что я не вижу в нем свойства даты?

Joakim Danielson 20.11.2022 09:17

Теперь все хорошо! Спасибо за помощь!

Arcgo 20.11.2022 22:03
struct Rates: Codable {
    let rates: [String: Point]
}


// MARK: - Point
struct Point: Codable {
    let eur: Double

    enum CodingKeys: String, CodingKey {
        case eur = "EUR"
    }
}

вы можете сделать что-то вроде этого

Спасибо за повтор! Евро - это динамическое имя, а не постоянное поле, но все же ценю вашу работу!

Arcgo 20.11.2022 22:07

Другие вопросы по теме