Home > Blockchain >  How to decode JSON in Swift?
How to decode JSON in Swift?

Time:11-20

I have an API JSON response like below. I want to decode the JSON to get an array of dictionary [String:Double], like [{"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
        }
    }
}

I have written decode code like below:

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)
        }

And I have tried to define a struct:

struct Rates: Codable, Hashable {
    let rates: Point
}

struct Point {

}

But I don't have an idea about what I should write in struct Point because the date is not a consistent field.

CodePudding user response:

struct Rates: Codable {
    let rates: [String: Point]
}


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

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

you can do something like this

CodePudding user response:

Here are two possible solutions, one with the struct Point and the other one with a dictionary instead

First the solution with Point

struct Point: Codable {
    let date: String
    let rate: Double
}

And then create a custom init(from:) where we decode the json into a dictionary first, [String: [String: Double]] and then map that dictionary into an array of 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) }
    }
}

And here is the second solution using a dictionary

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 }
    }
}
  • Related