Swift 4解码复杂嵌套JSON

Swift 4 decode complex nested JSON

我正在尝试创建一个更清晰的可编码结构,在其中我可以通过键入 day.description 而不是 day.weather.description

来访问 "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)
    }
}

如您所知,最简单的方法就是计算 属性 并引用所需的值。但是,为了完整起见,我们不妨讨论如何做您实际要求做的事情。让我们用你的 JSON:

的简化版本来说明
{
  "dt": 1558321200,
  "weather": [{
    "id": 800,
    "main": "Clear",
    "description": "clear sky",
    "icon": "01n"
  }]
}

所以问题是,我们如何将其解析为具有 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 调用。我们如何处理结果字符串取决于我们,所以现在我们可以将它插入我们的 top-level 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,只是摘录),所以我无法给出更准确的建议,但我相信您可以看到如何根据您的需要调整此技术。