0

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

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

{
"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)")
    }
4

1 回答 1

2

解决问题的一种方法,同时仍然具有仅使用 Codable 的好处,是创建一个与您获得的响应相对应的类型,它具有您需要的属性(当前天气的 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)
}
于 2018-03-05T17:56:36.090 回答