如何解决解码字典中的字典-JSON / Swift
我有一个结构如下的JSON文件:
"forecast": [
{
"altimeter": "","clouds": [
{
"repr": "OVC025","type": "OVC","altitude": 25,"modifier": null,"direction": null
}
],"flight_rules": "MVFR","other": [],"sanitized": "0318/0424 34017G26KT P6SM -RA OVC025","visibility": {
"repr": "P6","value": null,"spoken": "greater than six"
},"wind_direction": {
"repr": "340","value": 340,"spoken": "three four zero"
}
And so on....
我正在尝试使用DispatchQueue访问信息并更新UI,但无法弄清楚如何从预测内部提取数据:clouds:repr(或类似的其他嵌套内容)。我可以成功提取数据,例如:预测:原始。我尝试了没有嵌套在第一个结构中的结构,但是它没有用(按预期,数据是内部的另一个索引)。
我的解码文件是:
//
// TAFData.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/24/20.
//
import Foundation
struct TAFDatas: Codable {
let flight_rules: String?
let time: TimeTAF?
let station: String?
let raw: String?
let forecast: [ForecastTAF?]
let end_time: ?
let wind_gust: ?
}
struct ForecastTAF: Codable {
let raw: String?
struct CloudsTAF: Codable {
let type: String
let altitude: Int
}
struct endTimeTAF: Codable {
let repr: String
}
struct WindSpeedTAF: Codable {
let value: Int
}
struct WindGustTAF: Codable {
let value: Int
}
struct WindDirectionTAF: Codable {
let repr: String
}
struct VisibilityTAF: Codable {
let repr: String
}
struct WxcodesTAF: Codable {
let value: String
}
struct StartTimeTAF: Codable {
let repr: String
}
struct EndTimeTAF: Codable {
let repr: String
}
}
struct TimeTAF: Codable {
let repr: String
}
我的解析文件是:
//
// TAFManager.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/24/20.
//
import Foundation
protocol TAFManagerDelegate : class {
func didUpdateTAF(_ weatherManager: TAFManager,weatherTAF: TAFModel)
func didFailWithErrorTAF(error: Error)
}
struct TAFManager {
let TAFURL = "https://avwx.rest/api/taf/"
weak var delegate : TAFManagerDelegate?
func fetchWeatherTAF (stationICAO: String) {
let TAFurlString = "\(TAFURL)\(stationICAO)?token=OVi45FiTDo1LmyodShfOfoizNe5m9wyuO6Mkc95AN-c"
performRequestTAF(with: TAFurlString)
}
func performRequestTAF (with TAFurlString: String) {
if let TAFurl = URL(string: TAFurlString) {
let session = URLSession(configuration: .default)
let taskTAF = session.dataTask(with: TAFurl) { (data,response,error) in
if error != nil {
self.delegate?.didFailWithErrorTAF(error: error!)
return
}
if let safeDataTAF = data {
if let weatherTAF = self.parseJSONTAF(safeDataTAF) {
self.delegate?.didUpdateTAF(self,weatherTAF: weatherTAF)
}
}
}
taskTAF.resume()
print(TAFurlString)
}
}
func parseJSONTAF(_ TAFData: Data) -> TAFModel? {
do {
let decoderTAF = JSONDecoder()
let decodedDataTAF = try decoderTAF.decode(TAFDatas.self,from: TAFData)
let cloudsTAF = decodedDataTAF.clouds
let wxcodesTAF = decodedDataTAF.wx_codes
let forecastTAF = decodedDataTAF.forecast
let lowCloudsTypeTAF = (cloudsTAF.count > 0 ? cloudsTAF[0]?.type : nil) ?? "N/A"
let midCloudsTypeTAF = (cloudsTAF.count > 1 ? cloudsTAF[1]?.type : nil) ?? "N/A"
let highCloudsTypeTAF = (cloudsTAF.count > 2 ? cloudsTAF[2]?.type : nil) ?? "N/A"
let lowCloudsAltTAF = (cloudsTAF.count > 0 ? cloudsTAF[0]?.altitude : nil) ?? 0
let midCloudsAltTAF = (cloudsTAF.count > 1 ? cloudsTAF[1]?.altitude : nil) ?? 0
let highCloudsAltTAF = (cloudsTAF.count > 2 ? cloudsTAF[2]?.altitude : nil) ?? 0
let reportingStationVarTAF = decodedDataTAF.station ?? "N/A"
let windGustValueTAF = decodedDataTAF.wind_gust?.value ?? 0
let windSpeedValueTAF = decodedDataTAF.wind_speed?.value ?? 0
let windDirectionValueTAF = decodedDataTAF.wind_direction?.repr ?? "N/A"
let visibilityValueTAF = decodedDataTAF.visibility?.repr ?? "N/A"
let flightRulesValueTAF = decodedDataTAF.flight_rules ?? "N/A"
let timeReportedTAF = decodedDataTAF.time?.repr ?? "N/A"
let firstWxCode1TAF = (wxcodesTAF.count > 0 ? wxcodesTAF[0]?.value : "N/A") ?? "N/A"
let startTimeTaf = decodedDataTAF.start_time?.repr ?? "N/A"
let endTimeTaf = (forecastTAF.count > 0 ? forecastTAF[0]? : nil) ?? "N/A"
let rawTAFData = (forecastTAF.count > 0 ? forecastTAF[0]?.raw : nil) ?? "N/A"
let weatherTAF = TAFModel(lowestCloudsTypeTAF: lowCloudsTypeTAF,lowestCloudsAltTAF: lowCloudsAltTAF,middleCloudsTypeTAF: midCloudsTypeTAF,middleCloudsAltTAF: midCloudsAltTAF,highestCloudsTypeTAF: highCloudsTypeTAF,highestCloudsAltTAF: highCloudsAltTAF,reportingStationTAF: reportingStationVarTAF,windGustTAF: windGustValueTAF,windSpeedTAF: windSpeedValueTAF,windDirectionTAF: windDirectionValueTAF,visibilityTAF: visibilityValueTAF,flightRulesTAF: flightRulesValueTAF,timeTAF: timeReportedTAF,startTimeTAF: startTimeTaf,endTimeTAF: endTimeTaf,firstWxCodeTAF: firstWxCode1TAF,rawTAF: rawTAFData)
delegate?.didUpdateTAF(self,weatherTAF: weatherTAF)
return weatherTAF
} catch {
delegate?.didFailWithErrorTAF(error: error)
return nil
}
}
}
我的模型文件是:
//
// WeatherModel.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/22/20.
//
import Foundation
struct WeatherModel {
let lowestCloudsType: String
let lowestCloudsAlt: Int
let middleCloudsType: String
let middleCloudsAlt: Int
let highestCloudsType: String
let highestCloudsAlt: Int
let reportingStation: String
let windGust: Int
let windSpeed: Int
let windDirection: String
let visibility: String
let flightRules: String
let time: String
let remarks: String
let altimeter: Double
let temperature: String
let dewpoint: String
let firstWxCode: String
var altToString1: String {
return String(format: "%u" + "00 ft",lowestCloudsAlt)
}
var altToString2: String {
return String(format: "%u" + "00 ft",middleCloudsAlt)
}
var altToString3: String {
return String(format: "%u" + "00 ft",highestCloudsAlt)
}
var windGustString: String {
return String(format: "%u" + "kt",windGust)
}
var windSpeedString: String {
return String(format: "%u" + "kt",windSpeed)
}
var altimeterString: String {
return String(format: "%.2f" + " inHg",altimeter as CVarArg)
}
var visUnits: String {
return visibility + " SM"
}
var degUnits: String {
return windDirection + "°"
}
var tempUnits: String {
return temperature + "°C"
}
var dewUnits: String {
return dewpoint + "°C"
}
var flightConditions: String {
switch flightRules {
case "VFR":
return "green"
case "MVFR":
return "blue"
case "IFR":
return "red"
case "LIFR":
return "purple"
default:
return "gray"
}
}
}
我认为主要的难题是进入那些数据键。一旦我到了那里,希望其余的一切都准备就绪。我非常感谢您的提前帮助,祝您生活愉快!
带注释的是我的新JSON
// MARK: - TAFData
struct TAFData: Codable {
let meta: MetaTAF?
let raw,station: String?
let time: TimeTAF?
let remarks: String?
let forecast: [ForecastTAF?]
let startTime,endTime: TimeTAF?
let maxTemp,minTemp: String?
let alts,temps: JSONNull?
enum CodingKeys: String,CodingKey {
case meta,raw,station,time,remarks,forecast
case startTime = "start_time"
case endTime = "end_time"
case maxTemp = "max_temp"
case minTemp = "min_temp"
case alts,temps,units
}
}
// MARK: - Time
struct TimeTAF: Codable {
let repr,dt: String
}
// MARK: - Forecast
struct ForecastTAF: Codable {
let altimeter: String
let clouds: [CloudTAF]
let flightRules: String
let other: [JSONAny]
let sanitized: String
let visibility,windDirection: VisibilityTAF
let windGust: VisibilityTAF?
let windSpeed: VisibilityTAF
let wxCodes: [WxCodeTAF]
let endTime: TimeTAF
let icing: [JSONAny]
let probability: JSONNull?
let raw: String
let startTime: TimeTAF
let turbulence: [JSONAny]
let type: String
let windShear: JSONNull?
let summary: String
enum CodingKeys: String,CodingKey {
case altimeter,clouds
case flightRules = "flight_rules"
case other,sanitized,visibility
case windDirection = "wind_direction"
case windGust = "wind_gust"
case windSpeed = "wind_speed"
case wxCodes = "wx_codes"
case endTime = "end_time"
case icing,probability,raw
case startTime = "start_time"
case turbulence,type
case windShear = "wind_shear"
case summary
}
}
// MARK: - Cloud
struct CloudTAF: Codable {
let repr,type: String
let altitude: Int
let modifier,direction: JSONNull?
}
// MARK: - Visibility
struct VisibilityTAF: Codable {
let repr: String
let value: Int?
let spoken: String
}
// MARK: - WxCode
struct WxCodeTAF: Codable {
let repr,value: String
}
// MARK: - Meta
struct MetaTAF: Codable {
let timestamp: String
}
// MARK: - Units
struct UnitsTAF: Codable {
let altimeter,altitude,temperature,visibility: String
let windSpeed: String
enum CodingKeys: String,visibility
case windSpeed = "wind_speed"
}
}
// MARK: - Encode/decode helpers
class JSONNull: Codable,Hashable {
public static func == (lhs: JSONNull,rhs: JSONNull) -> Bool {
return true
}
public var hashValue: Int {
return 0
}
public func hash(into hasher: inout Hasher) {
// No-op
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self,DecodingError.Context(codingPath: decoder.codingPath,debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
class JSONCodingKey: CodingKey {
let key: String
required init?(intValue: Int) {
return nil
}
required init?(stringValue: String) {
key = stringValue
}
var intValue: Int? {
return nil
}
var stringValue: String {
return key
}
}
class JSONAny: Codable {
let value: Any
static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
let context = DecodingError.Context(codingPath: codingPath,debugDescription: "Cannot decode JSONAny")
return DecodingError.typeMismatch(JSONAny.self,context)
}
static func encodingError(forValue value: Any,codingPath: [CodingKey]) -> EncodingError {
let context = EncodingError.Context(codingPath: codingPath,debugDescription: "Cannot encode JSONAny")
return EncodingError.invalidValue(value,context)
}
static func decode(from container: SingleValueDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if container.decodeNil() {
return JSONNull()
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if let value = try? container.decodeNil() {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer() {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>,forKey key: JSONCodingKey) throws -> Any {
if let value = try? container.decode(Bool.self,forKey: key) {
return value
}
if let value = try? container.decode(Int64.self,forKey: key) {
return value
}
if let value = try? container.decode(Double.self,forKey: key) {
return value
}
if let value = try? container.decode(String.self,forKey: key) {
return value
}
if let value = try? container.decodeNil(forKey: key) {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer(forKey: key) {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self,forKey: key) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
var arr: [Any] = []
while !container.isAtEnd {
let value = try decode(from: &container)
arr.append(value)
}
return arr
}
static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
var dict = [String: Any]()
for key in container.allKeys {
let value = try decode(from: &container,forKey: key)
dict[key.stringValue] = value
}
return dict
}
static func encode(to container: inout UnkeyedEncodingContainer,array: [Any]) throws {
for value in array {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer()
try encode(to: &container,array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
try encode(to: &container,dictionary: value)
} else {
throw encodingError(forValue: value,codingPath: container.codingPath)
}
}
}
static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>,dictionary: [String: Any]) throws {
for (key,value) in dictionary {
let key = JSONCodingKey(stringValue: key)!
if let value = value as? Bool {
try container.encode(value,forKey: key)
} else if let value = value as? Int64 {
try container.encode(value,forKey: key)
} else if let value = value as? Double {
try container.encode(value,forKey: key)
} else if let value = value as? String {
try container.encode(value,forKey: key)
} else if value is JSONNull {
try container.encodeNil(forKey: key)
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer(forKey: key)
try encode(to: &container,array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self,forKey: key)
try encode(to: &container,codingPath: container.codingPath)
}
}
}
static func encode(to container: inout SingleValueEncodingContainer,value: Any) throws {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else {
throw encodingError(forValue: value,codingPath: container.codingPath)
}
}
public required init(from decoder: Decoder) throws {
if var arrayContainer = try? decoder.unkeyedContainer() {
self.value = try JSONAny.decodeArray(from: &arrayContainer)
} else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
self.value = try JSONAny.decodeDictionary(from: &container)
} else {
let container = try decoder.singleValueContainer()
self.value = try JSONAny.decode(from: container)
}
}
public func encode(to encoder: Encoder) throws {
if let arr = self.value as? [Any] {
var container = encoder.unkeyedContainer()
try JSONAny.encode(to: &container,array: arr)
} else if let dict = self.value as? [String: Any] {
var container = encoder.container(keyedBy: JSONCodingKey.self)
try JSONAny.encode(to: &container,dictionary: dict)
} else {
var container = encoder.singleValueContainer()
try JSONAny.encode(to: &container,value: self.value)
}
}
}
解决方法
我可能会使用不同的模型,因为您当前使用的模型不支持与JSON相同的格式:
struct Forecast: Codable {
let altimeter: String
let clouds: [Cloud]
let flightRules: String
let other: [JSONAny]
let sanitized: String
let visibility,windDirection: Visibility
enum CodingKeys: String,CodingKey {
case altimeter,clouds
case flightRules = "flight_rules"
case other,sanitized,visibility
case windDirection = "wind_direction"
}
}
// MARK: - Cloud
struct Cloud: Codable {
let repr,type: String
let altitude: Int
}
// MARK: - Visibility
struct Visibility: Codable {
let repr: String
let value: Int?
let spoken: String
}
这样您就可以使用:
if let cloud = forecast.clouds.first() {
let repr = cloud.repr
.. Your logic
}
我的模型中可能缺少一些东西,但是您应该有个主意
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。