Свифт 4 декодирует сложный вложенный JSON - PullRequest
0 голосов
/ 18 мая 2019

Я пытаюсь создать более понятную кодируемую структуру, в которой я могу получить доступ к «описанию», просто набрав day.description вместо day.weather.description

Значение описания вложено в массив «weather»который содержит только один объект.Я хотел бы извлечь описание из индекса 0 и назначить его для описания в моей структуре.

Вот JSON, с которым я работаю:


{
    "dt": 1558321200,
    "main": {
        "temp": 11.88,
        "temp_min": 11.88,
        "temp_max": 11.88,
        "pressure": 1013.3,
        "sea_level": 1013.3,
        "grnd_level": 1003.36,
        "humidity": 77,
        "temp_kf": 0
    },
    "weather": [{
        "id": 800,
        "main": "Clear",
        "description": "clear sky",
        "icon": "01n"
    }],
    "clouds": {
        "all": 0
    },
    "wind": {
        "speed": 5.58,
        "deg": 275.601
    },
    "sys": {
        "pod": "n"
    },
    "dt_txt": "2019-05-20 03:00:00"
}


и код, который у меня есть:


struct Weather: Codable {
    let days: [Day]

    enum CodingKeys: String, CodingKey {
        case days = "list"
    }
}

struct Day: Codable {
    let date: String
    let main: Main
    let wind: Wind
    let description: String

    enum CodingKeys: String, CodingKey {
        case date = "dt_txt"
        case main
        case wind
        case weather
        case description
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        date = try container.decode(String.self, forKey: .date)
        main = try container.decode(Main.self, forKey: .main)
        wind = try container.decode(Wind.self, forKey: .wind)
        let weather = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .weather)
        description = try weather.decode(String.self, forKey: .description)
    }
}

1 Ответ

0 голосов
/ 18 мая 2019

Как вам сказали, самый простой подход - это просто вычисляемое свойство, которое ссылается на желаемое значение.Однако для полноты картины мы могли бы также обсудить, как сделать то, что вы на самом деле просили.Давайте проиллюстрируем сокращенной версией вашего JSON:

{
  "dt": 1558321200,
  "weather": [{
    "id": 800,
    "main": "Clear",
    "description": "clear sky",
    "icon": "01n"
  }]
}

Итак, вопрос в том, как мы можем проанализировать это, скажем, в виде структуры Result со свойством description, таким образом, чтобы мы получили "description" ключ от первого элемента в массиве "weather"?Вот один из подходов:

struct Result : Decodable {
    let description : String
    enum Keys : CodingKey {
        case weather
    }
    struct Weather : Decodable {
        let description : String
    }
    init(from decoder: Decoder) throws {
        let con = try! decoder.container(keyedBy: Keys.self)
        var arr = try! con.nestedUnkeyedContainer(forKey: .weather) // weather array
        let oneWeather = try! arr.decode(Weather.self) // decode first element
        self.description = oneWeather.description
    }
}

По сути, идея в том, что nestedUnkeyedContainer дает нам наш массив, и последующие вызовы decode в этом массиве автоматически решают каждый элемент по очереди.У нас есть только один элемент, поэтому нам нужен только один вызов decode.Как мы распоряжаемся полученной строкой, зависит от нас, поэтому теперь мы можем вставить ее в наше свойство description верхнего уровня.

Но вот другой подход.Нам даже не нужна вторичная структура Weather;мы можем просто погрузиться прямо в массив "weather", взять первый элемент словаря и получить доступ к его ключу "description", не говоря больше о том, что находится в этом внутреннем словаре, например:

struct Result : Decodable {
    let description : String
    enum Keys : CodingKey {
        case weather
        case description
    }
    init(from decoder: Decoder) throws {
        let con = try! decoder.container(keyedBy: Keys.self)
        var arr = try! con.nestedUnkeyedContainer(forKey: .weather)
        let con2 = try! arr.nestedContainer(keyedBy: Keys.self)
        let desc = try! con2.decode(String.self, forKey: .description)
        self.description = desc
    }
}

Ваш вопрос былне очень полный (вы не показали свой реальный JSON, просто отрывок), поэтому я не могу дать более точный совет, но я уверен, что вы сможете увидеть, как адаптировать эту технику к вашим потребностям.

...