1

我想解析普利茅斯气象局的天气数据。我拥有的结构如下:

struct WeatherRoot: Codable {
    var siteRep: SiteRep

    private enum CodingKeys: String, CodingKey {
        case siteRep = "SiteRep"
    }
}

struct SiteRep: Codable {
    var dataWx: DataWx
    var dataDV: DataDV

    private enum CodingKeys: String, CodingKey {
        case dataWx = "Wx"
        case dataDV = "DV"
    }
}

struct DataWx: Codable {
    var param: [Param]?

    private enum CodingKeys: String, CodingKey {
        case param = "Param"
    }
}

struct Param: Codable {
    var headings: WeatherDataHeadings

    private enum CodingKeys: String, CodingKey {
        case headings = "Param"
    }
}

struct WeatherDataHeadings: Codable {
    var name: String
    var unit: String
    var title: String

    private enum CodingKeys: String, CodingKey {
        case name = "name"
        case unit = "units"
        case title = "$"
    }
}

struct DataDV: Codable {
    var dataDate: String
    var type: String
    var location: LocationDetails

    private enum CodingKeys: String, CodingKey {
        case dataDate = "dataType"
        case type = "type"
        case location = "Location"
    }
}

struct LocationDetails: Codable {
    var id: String
    var latitude: String
    var longitude: String
    var name: String
    var country: String
    var continent: String
    var elevation: String
    var period: [Period]

    private enum CodingKeys: String, CodingKey {
        case id = "i"
        case latitude = "lat"
        case longitude = "lon"
        case name
        case country
        case continent
        case elevation
        case period = "Period"
    }
}

struct Period: Codable {
    var type: String
    var value: String
    var rep: [Rep]

    private enum CodingKeys: String, CodingKey {
        case type = "type"
        case value = "value"
        case rep = "Rep"
    }
}

struct Rep: Codable {
    var windDirection: String
    var feelsLikeTemperature: String
    var windGust: String
    var humidity: String
    var precipitation: String
    var windSpeed: String
    var temperature: String
    var visibility: String
    var weatherType: String
    var uvIndex: String
    var time: String

    private enum CodingKeys: String, CodingKey {
        case windDirection = "D"
        case feelsLikeTemperature = "F"
        case windGust = "G"
        case humidity = "H"
        case precipitation = "Pp"
        case windSpeed = "S"
        case temperature = "T"
        case visibility = "V"
        case weatherType = "W"
        case uvIndex = "U"
        case time = "$"
    }
}

extension WeatherRoot {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        siteRep = try values.decode(SiteRep.self, forKey: .siteRep)
    }
}

extension SiteRep {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        dataWx = try values.decode(DataWx.self, forKey: .dataWx)
        dataDV = try values.decode(DataDV.self, forKey: .dataDV)
    }
}

extension DataWx {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        param = try values.decodeIfPresent([Param].self, forKey: .param)
    }
}

extension Param {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        headings = try values.decode(WeatherDataHeadings.self, forKey: .headings)
    }
}

extension WeatherDataHeadings {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        unit = try values.decode(String.self, forKey: .unit)
        title = try values.decode(String.self, forKey: .title)
    }
}

extension DataDV {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        dataDate = try values.decode(String.self, forKey: .dataDate)
        type = try values.decode(String.self, forKey: .type)
        location = try values.decode(LocationDetails.self, forKey: .location)
    }
}

extension LocationDetails {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(String.self, forKey: .id)
        latitude = try values.decode(String.self, forKey: .latitude)
        longitude = try values.decode(String.self, forKey: .longitude)
        name = try values.decode(String.self, forKey: .name)
        country = try values.decode(String.self, forKey: .country)
        continent = try values.decode(String.self, forKey: .continent)
        elevation = try values.decode(String.self, forKey: .elevation)
        period = try [values.decode(Period.self, forKey: .period)]
    }
}

extension Period {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        type = try values.decode(String.self, forKey: .type)
        value = try values.decode(String.self, forKey: .value)
        rep = try [values.decode(Rep.self, forKey: .rep)]
    }
}

extension Rep {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        windDirection = try values.decode(String.self, forKey: .windDirection)
        feelsLikeTemperature = try values.decode(String.self, forKey: .feelsLikeTemperature)
        windGust = try values.decode(String.self, forKey: .windGust)
        humidity = try values.decode(String.self, forKey: .humidity)
        precipitation = try values.decode(String.self, forKey: .precipitation)
        windSpeed = try values.decode(String.self, forKey: .windSpeed)
        temperature = try values.decode(String.self, forKey: .temperature)
        visibility = try values.decode(String.self, forKey: .visibility)
        weatherType = try values.decode(String.self, forKey: .weatherType)
        uvIndex = try values.decode(String.self, forKey: .uvIndex)
        time = try values.decode(String.self, forKey: .time)
    }
}

我试图解析的数据是:

{"SiteRep":{"Wx":{"Param":[{"name":"F","units":"C","$":"Feels Like Temperature"},{"name":"G","units":"mph","$":"Wind Gust"},{"name":"H","units":"%","$":"Screen Relative Humidity"},{"name":"T","units":"C","$":"Temperature"},{"name":"V","units":"","$":"Visibility"},{"name":"D","units":"compass","$":"Wind Direction"},{"name":"S","units":"mph","$":"Wind Speed"},{"name":"U","units":"","$":"Max UV Index"},{"name":"W","units":"","$":"Weather Type"},{"name":"Pp","units":"%","$":"Precipitation Probability"}]},"DV":{"dataDate":"2018-03-16T19:00:00Z","type":"Forecast","Location":{"i":"3844","lat":"50.7366","lon":"-3.40458","name":"EXETER AIRPORT 2","country":"ENGLAND","continent":"EUROPE","elevation":"27.0","Period":[{"type":"Day","value":"2018-03-16Z","Rep":[{"D":"SE","F":"8","G":"16","H":"78","Pp":"6","S":"11","T":"11","V":"EX","W":"7","U":"1","$":"900"},{"D":"SE","F":"6","G":"11","H":"88","Pp":"6","S":"9","T":"8","V":"MO","W":"7","U":"1","$":"1080"},{"D":"E","F":"5","G":"13","H":"92","Pp":"5","S":"4","T":"7","V":"GO","W":"7","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-17Z","Rep":[{"D":"E","F":"5","G":"16","H":"90","Pp":"86","S":"7","T":"7","V":"GO","W":"12","U":"0","$":"0"},{"D":"ENE","F":"5","G":"13","H":"93","Pp":"82","S":"7","T":"7","V":"GO","W":"15","U":"0","$":"180"},{"D":"ENE","F":"2","G":"22","H":"91","Pp":"40","S":"11","T":"6","V":"MO","W":"9","U":"0","$":"360"},{"D":"NE","F":"-2","G":"29","H":"84","Pp":"44","S":"16","T":"3","V":"VG","W":"12","U":"1","$":"540"},{"D":"ENE","F":"-4","G":"29","H":"75","Pp":"17","S":"16","T":"2","V":"VG","W":"8","U":"2","$":"720"},{"D":"ENE","F":"-4","G":"29","H":"72","Pp":"20","S":"16","T":"2","V":"VG","W":"8","U":"1","$":"900"},{"D":"NE","F":"-6","G":"25","H":"73","Pp":"17","S":"13","T":"0","V":"VG","W":"8","U":"1","$":"1080"},{"D":"NE","F":"-7","G":"22","H":"81","Pp":"16","S":"11","T":"-1","V":"VG","W":"8","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-18Z","Rep":[{"D":"NE","F":"-8","G":"22","H":"86","Pp":"51","S":"11","T":"-2","V":"VG","W":"24","U":"0","$":"0"},{"D":"NE","F":"-8","G":"22","H":"87","Pp":"60","S":"11","T":"-2","V":"GO","W":"24","U":"0","$":"180"},{"D":"NE","F":"-8","G":"25","H":"88","Pp":"66","S":"13","T":"-1","V":"MO","W":"24","U":"0","$":"360"},{"D":"ENE","F":"-8","G":"29","H":"92","Pp":"84","S":"16","T":"-1","V":"PO","W":"27","U":"1","$":"540"},{"D":"ENE","F":"-5","G":"31","H":"84","Pp":"63","S":"16","T":"1","V":"MO","W":"24","U":"2","$":"720"},{"D":"ENE","F":"-5","G":"29","H":"83","Pp":"26","S":"16","T":"1","V":"MO","W":"8","U":"1","$":"900"},{"D":"ENE","F":"-6","G":"25","H":"80","Pp":"24","S":"13","T":"0","V":"GO","W":"8","U":"1","$":"1080"},{"D":"ENE","F":"-7","G":"25","H":"78","Pp":"18","S":"13","T":"-1","V":"GO","W":"8","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-19Z","Rep":[{"D":"NE","F":"-8","G":"25","H":"78","Pp":"12","S":"11","T":"-2","V":"VG","W":"7","U":"0","$":"0"},{"D":"NE","F":"-8","G":"25","H":"78","Pp":"10","S":"13","T":"-2","V":"VG","W":"7","U":"0","$":"180"},{"D":"NE","F":"-8","G":"22","H":"77","Pp":"11","S":"11","T":"-2","V":"VG","W":"7","U":"0","$":"360"},{"D":"NE","F":"-7","G":"27","H":"69","Pp":"3","S":"13","T":"0","V":"VG","W":"3","U":"1","$":"540"},{"D":"ENE","F":"-3","G":"29","H":"57","Pp":"2","S":"16","T":"3","V":"VG","W":"3","U":"3","$":"720"},{"D":"NE","F":"0","G":"29","H":"49","Pp":"1","S":"16","T":"5","V":"VG","W":"1","U":"1","$":"900"},{"D":"NE","F":"-1","G":"20","H":"59","Pp":"1","S":"11","T":"4","V":"VG","W":"1","U":"1","$":"1080"},{"D":"NNE","F":"-4","G":"22","H":"73","Pp":"1","S":"11","T":"2","V":"VG","W":"0","U":"0","$":"1260"}]},{"type":"Day","value":"2018-03-20Z","Rep":[{"D":"NNE","F":"-4","G":"18","H":"81","Pp":"5","S":"9","T":"1","V":"VG","W":"7","U":"0","$":"0"},{"D":"N","F":"-3","G":"18","H":"86","Pp":"5","S":"9","T":"2","V":"GO","W":"7","U":"0","$":"180"},{"D":"N","F":"-3","G":"18","H":"88","Pp":"5","S":"9","T":"2","V":"GO","W":"7","U":"0","$":"360"},{"D":"N","F":"0","G":"20","H":"78","Pp":"5","S":"9","T":"4","V":"VG","W":"7","U":"1","$":"540"},{"D":"NNE","F":"3","G":"22","H":"68","Pp":"1","S":"11","T":"7","V":"VG","W":"3","U":"3","$":"720"},{"D":"N","F":"5","G":"22","H":"62","Pp":"5","S":"11","T":"8","V":"VG","W":"7","U":"1","$":"900"},{"D":"NNW","F":"3","G":"13","H":"72","Pp":"5","S":"7","T":"6","V":"VG","W":"7","U":"1","$":"1080"},{"D":"NNW","F":"1","G":"11","H":"82","Pp":"5","S":"4","T":"4","V":"GO","W":"7","U":"0","$":"1260"}]}]}}}}

但是,当我将 JSON 解码为结构时,出现错误:

Error Serializing Json: keyNotFound(Clothing_Prediction_iOS_Application.Param.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).headings, Swift.DecodingError.Context(codingPath: [Clothing_Prediction_iOS_Application.WeatherRoot.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).siteRep, Clothing_Prediction_iOS_Application.SiteRep.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).dataWx, Clothing_Prediction_iOS_Application.DataWx.(CodingKeys in _99F6E563F35EF627A75B06F8891FEB0F).param, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0))], debugDescription: "No value associated with key headings (\"Param\").", underlyingError: nil))

我曾尝试查看可选选项,但我不明白为什么会出现此错误。

4

2 回答 2

0

如果您解析错误消息(我知道很乱),您会发现它告诉您两件事:

  • 未找到密钥:headings
  • 在关键路径:siteRep/dataWx/param[0]

关键路径是数据模型中属性的名称。如果您将它们转换回您在各种CodingKeyss 中的映射方式,您将获得 JSON 路径:SiteRep/Wx/Param[0]. 那里没有headings找到。

如何修复它:

  1. 删除您当前的Param结构
  2. 重命名WeatherDataHeadingsParam

您还有另一个映射错误DataDV

struct DataDV: Codable {
    // Better parse date as Date, and not as String. This
    // requires set the dateDecodingStrategy. See below.
    var dataDate: Date
    var type: String
    var location: LocationDetails

    private enum CodingKeys: String, CodingKey {
        case dataDate = "dataDate" // not "dataType"
        case type = "type"
        case location = "Location"
    }
}

你写的代码比需要的多。您可以删除所有这些扩展。编译器可以为您合成它们。


下面是你如何解码它:

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let weatherRoot = try decoder.decode(WeatherRoot.self, from: jsonData)
于 2018-03-17T01:20:09.127 回答
0

看起来您正在复制Param数据结构。DataWxcase param = "Param",没关系。但是Paramhas case headings = "Param",这不在您的 JSON 中。所以你的 JSON 从

{"SiteRep":{"Wx":{"Param":[{"name"...

但是您的数据结构期望像

{"SiteRep":{"Wx":{"Param":{"Param":...

错误不是很清楚,但这似乎是它试图告诉你的。

于 2018-03-16T21:00:49.097 回答