Разбор различных JSON фидов в Swift для одной и той же Декодируемой структуры - PullRequest
2 голосов
/ 01 февраля 2020

У меня есть два разных JSON канала,

{
 "uid":9018823,
 "lat":43.25394,
 "lng":-2.93844,
 "bike":false,
 "name":"02-STATION",
 "address":null,
 "spot":true,
 "number":3388,
 "bikes":3,
 "booked_bikes":0,
 "bike_racks":20,
 "free_racks":16
}

и

{
 "address":null,"last_updated":1580546431,
 "renting":1,"returning":1,"uid":"3348"},
 "free_bikes":17,
 "id":"0a0d9d6e93abd05548c672b60bfa9099",
 "latitude":40.677236,
 "longitude":-74.015665,
 "station_name":"Coffey St & Conover St",
 "timestamp":"2020-02-01T09:26:31.254000Z"
}

Я хотел бы проанализировать оба канала, заполнив следующую структуру,


struct Places: Codable {

    var name: String
    let lat: Double
    let lng: Double
}

Как я видел, я могу сделать это, используя Decodable в init(from decoder:Decoder), но я не могу обернуть его вокруг и заставить его работать.

1 Ответ

3 голосов
/ 01 февраля 2020

Одним из способов решения этой проблемы является наличие перечисления CodingKey для каждого типа сообщения json, а затем при попытке создать контейнер с использованием одного перечисления мы отлавливаем любую ошибку, а не генерируем ее, и пытаемся создать контейнер с использованием другого перечисления

struct Places: Decodable {
    let name: String
    let lat: Double
    let lng: Double

    enum CodingKeys1: String, CodingKey {
        case name
        case lat
        case lng
    }

    enum CodingKeys2: String, CodingKey {
        case name = "station_name"
        case lat = "latitude"
        case lng = "longitude"
    }

    init(from decoder: Decoder) throws {
        do {
            let container = try decoder.container(keyedBy: CodingKeys1.self)
            try self.init(container)
        } catch {
            let container = try decoder.container(keyedBy: CodingKeys2.self)
            try self.init(container)
        }
    }

    private init(_ container: KeyedDecodingContainer<CodingKeys1>) throws {
        name = try container.decode(String.self, forKey: .name)
        lat = try container.decode(Double.self, forKey: .lat)
        lng = try container.decode(Double.self, forKey: .lng)
    }

    private init(_ container: KeyedDecodingContainer<CodingKeys2>) throws {
        name = try container.decode(String.self, forKey: .name)
        lat = try container.decode(Double.self, forKey: .lat)
        lng = try container.decode(Double.self, forKey: .lng)
    }
}

Возможно, последняя часть может быть переписана с использованием обобщений.

Вот пример, где data1 и data2 - две выборки из вопроса

do {

    for data in [data1, data2] {
        let result = try JSONDecoder().decode(Places.self, from: data)
        print(result)
    }
} catch {
    print(error)
}

Места (имя: "02-STATION", широта: 43.25394, lng: - 2.93844)
Места (название: "Coffey St & Conover St", шир.: 40.677236, lng: -74.015665)

...