使用decodable解析来自同一json-swift中两个不同容器的数据

问题描述 投票:0回答:1

我想知道是否有可能从json文件解析数据以创建一个类型,其中json文件在json数据结构中的多个位置存储要创建的类型的多个对象。我的意思是使用下面的示例json我想创建两个“天气”对象。一个来自“Currently”容器,另一个来自“daily”>“Data”容器。我将在下面发布我的代码,用于获取“当前”数据,但我不确定如何使用相同的Weather结构然后从每日容器中获取数据。我想最终得到一个带有两个对象的Weather类型数组,一个来自“current”,另一个来自“daily”容器。

附:我知道解决这个问题的一种方法是有两个不同的结构或两个不同类别的天气,一个用于当前,另一个用于预测。假设这可能更好地定义数据是什么,但有兴趣知道我上面描述的内容是否可行。

{
"currently": {
    "time": 1520250330,
    "summary": "Clear",
    "icon": "clear-night",
    "nearestStormDistance": 198,
    "nearestStormBearing": 16,
    "precipIntensity": 0,
    "precipProbability": 0,
    "temperature": 41.42,
    "apparentTemperature": 41.42,
    "dewPoint": 33.95,
    "humidity": 0.75,
    "pressure": 1027.07,
    "windSpeed": 2.89,
    "windGust": 5.43,
    "windBearing": 39,
    "cloudCover": 0,
    "uvIndex": 0,
    "visibility": 10,
    "ozone": 346.58
},
"daily": {
    "summary": "Light rain on Wednesday, with temperatures falling to 58°F on Sunday.",
    "icon": "rain",
    "data": [
        {
            "time": 1520236800,
            "summary": "Partly cloudy overnight.",
            "icon": "partly-cloudy-night",
            "sunriseTime": 1520260588,
            "sunsetTime": 1520302114,
            "moonPhase": 0.64,
            "precipIntensity": 0.0002,
            "precipIntensityMax": 0.0018,
            "precipIntensityMaxTime": 1520283600,
            "precipProbability": 0.13,
            "precipType": "rain",
            "temperatureHigh": 58.02,
            "temperatureHighTime": 1520298000,
            "temperatureLow": 46.48,
            "temperatureLowTime": 1520348400,
            "apparentTemperatureHigh": 58.02,
            "apparentTemperatureHighTime": 1520298000,
            "apparentTemperatureLow": 42.09,
            "apparentTemperatureLowTime": 1520348400,
            "dewPoint": 36.4,
            "humidity": 0.62,
            "pressure": 1024.6,
            "windSpeed": 4.65,
            "windGust": 15.36,
            "windGustTime": 1520269200,
            "windBearing": 35,
            "cloudCover": 0.08,
            "uvIndex": 5,
            "uvIndexTime": 1520280000,
            "visibility": 10,
            "ozone": 340.2,
            "temperatureMin": 41.36,
            "temperatureMinTime": 1520254800,
            "temperatureMax": 58.02,
            "temperatureMaxTime": 1520298000,
            "apparentTemperatureMin": 37.53,
            "apparentTemperatureMinTime": 1520262000,
            "apparentTemperatureMax": 58.02,
            "apparentTemperatureMaxTime": 1520298000
        }

当前结构:

struct Weather: Decodable {
let temperature: Double
var temperatureCelsius: Double {
    let temp = 5 / 9 * (temperature - 32) as Double
    return Double(round(temp))
}
let humidity: Double
let rainProbability: Int
let summary: String
let icon: String
let temperatureMax: Double?
let temperatureMin: Double?

private enum CodingKeys: String, CodingKey {
    case temperature
    case humidity
    case rainProbability = "precipProbability"
    case summary
    case icon
    case temperatureMax
    case temperatureMin
}

private enum CurrentlyKeys: String, CodingKey {
    case currently
}

public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CurrentlyKeys.self)
    let weatherValues = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .currently)
    temperature = try weatherValues.decode(Double.self, forKey: .temperature)
    humidity = try weatherValues.decode(Double.self, forKey: .humidity)
    rainProbability = try weatherValues.decode(Int.self, forKey: .rainProbability)
    summary = try weatherValues.decode(String.self, forKey: .summary)
    icon = try weatherValues.decode(String.self, forKey: .icon)
    temperatureMax = try weatherValues.decodeIfPresent(Double.self, forKey: .temperatureMax)
    temperatureMin = try weatherValues.decodeIfPresent(Double.self, forKey: .temperatureMin)
}

解析数据:

do {
      let currentWeather = try JSONDecoder().decode(Weather.self, from: data)
      print(currentWeather.summary)
    } catch let error {
      print("error: \(error)")
    }
json swift parsing decodable
1个回答
2
投票

解决这个问题同时还有使用Codable的好处的一种方法是创建一个与你得到的响应相对应的类型,它具有你需要的属性(currentWeather的Weather对象和预测的Weather对象数组) )。

在这种特定情况下,它有点复杂,因为预测数组包含在另一个JSON对象中。您可以通过提供自定义解码实现而不是使用Codable生成的实现来解决此问题:

struct WeatherApiResponse: Decodable {

    fileprivate struct DailyContainer: Decodable {
        let data: [Weather]
    }

    let currentWeather: Weather
    let dailyWeather: [Weather]

    init(from decoder: Decoder) throws {
        let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
        currentWeather = try keyedContainer.decode(Weather.self, forKey: .currentWeather)
        dailyWeather = (try keyedContainer.decode(DailyContainer.self, forKey: .dailyWeather)).data
    }

    enum CodingKeys: String, CodingKey {
        case currentWeather = "current"
        case dailyWeather = "daily"
    }
}

并解码您需要的两个属性,如下所示:

if let response = try? jsonDecoder.decode(WeatherApiResponse.self, from: data) {
    print(response.currentWeather)
    print(response.dailyWeather)
}
© www.soinside.com 2019 - 2024. All rights reserved.