I'm new to JSON parsing. I'm trying to retrieve weather data using an API. Users input a city name as a textfield string, which will then be passed into a networking method that should retrieve the temperature for this city. The JSON looks like this:
"remainingCost": 0,
"queryCost": 1,
"messages": null,
"locations": {
"London": {
"stationContributions": null,
"values": [
{
"wdir": 322.4,
"uvindex": 3.0,
"datetimeStr": "2022-09-28T17:00:00 01:00",
"preciptype": "",
"cin": -2.0,
"cloudcover": 57.4,
"pop": 19.0,
"datetime": 1664384400000,
"precip": 0.0,
"solarradiation": 280.0,
"dew": 4.0,
"humidity": 49.9,
"temp": 14.3,
"visibility": 15.0,
"wspd": 6.0,
"severerisk": 10.0,
"solarenergy": 1.0,
"heatindex": null,
"snowdepth": 0.0,
"sealevelpressure": 1000.0,
"snow": 0.0,
"wgust": 15.4,
"conditions": "Partially cloudy",
"windchill": null,
"cape": 11.0
}
Now, I've learned that I can build structs to host the decoded JSON data once I retrieve it, and I've created the following:
struct WeatherModel: Decodable {
let locations: Locations
}
struct Locations: Decodable {
let London: London
}
struct London: Decodable {
let values: [Values]
}
struct Values: Decodable {
let temp: Double
}
Then, using my decoder, I am able to successfully retrieve "temp". So far so good.
The issue is that "temp" is contained inside "values" which is contained inside "London", which is then contained inside "locations". However 'London' should not be called each time, only if the user enters 'London' in the textfield. If they entered 'Paris', for example, then my structs would no longer work, and as far as I know I can't make a 'variable' struct that would accept any city name as its name.
How can I resolve this so that I can get the different city weather depending on the input? Is there a different way of housing the JSON? Is this a shortcoming of the API itself, and maybe I should find a different data source? Thanks in advance for your advice.
CodePudding user response:
For this one, I suggest you create the object model like this:
struct WeatherModel: Decodable {
let locations: [String: Any?]
}
struct City: Decodable {
let values: [Values]
}
struct Values: Decodable {
let temp: Double
}
And when you get the WeatherModel
it will include locations
as the [String: Any?]
. Now, get the dictionary of the city by using the key is city name.
let cityDictionary = weatherModel.locations["cityName"] as? [String: Any?]
The last thing is using JSONSerialization
to decode the dictionary into the object, or get the object value key by key.
CodePudding user response:
import Foundation
// MARK: - MyObject
struct MyObject: Codable {
let remainingCost, queryCost: Int
let messages: String?
///Dictionary that will have the city name
///as the key and the values as an array of objects.
let locations:[String,[City]]
}
// MARK: - London
struct City: Codable {
let stationContributions: String?
let values: [Value]
}
// MARK: - Value
struct Value: Codable {
let wdir: Double
let uvindex: Int
let datetimeStr: Date
let preciptype: String
let cin: Int
let cloudcover: Double
let pop, datetime, precip, solarradiation: Int
let dew: Int
let humidity, temp: Double
let visibility, wspd, severerisk, solarenergy: Int
let heatindex: Double? //Check unclear based one sample
let snowdepth, sealevelpressure, snow: Int
let wgust: Double
let conditions: String
let windchill: Double? //Check unclear based one sample
let cape: Int
}