简体   繁体   中英

How to organise JSON Structs in an array

I have used JSON structs before and managed to get them working with a different API, but this API's JSON data is slightly different, it seems to encompass the data in an array called 'List'. I assume it is the Structs below that are in the incorrect format? As when I run the app, I don't get any error messages, but the Label value that I am trying to change, does not change, nor does the value of 'Test' get printed to the console. I am trying to call the Description value and print it to a label.

JSON Structs below:

struct MyForecast : Decodable {
    let cod : String
    let message : Double
    let cnt : Int
    let list : [List]
    let city : Cityy
    let coordinate : Coordi
}

struct Coordi : Decodable {
    let lat, lon : Double
}

struct Cityy : Decodable {
    let id, population : Int
    let name, country : String
    let coord : Coordinate
}

struct Mainn : Decodable {
    let temp, tempMin, tempMax : Double
    let seaLevel, grndLevel, tempKf: Double
    let pressure, humidity : Int
}

struct Windd : Decodable {
    let speed : Double
    let deg : Double
}

struct Weatherr : Decodable {
    let id : Int
    let icon : String
    let main : MainEnum
    let description: String
}

struct List : Decodable {
    let dt : Date
    let main : MainForecast
    let weather : [Weatherr]
    let clouds : Cloudss
    let wind : Windd
    let sys : Syss
    let dtTxt : String
    let rain: Rainn?
    let city: Cityy
}

struct Syss : Decodable {
    let pod: Pod
}

struct MainForecast : Decodable {
    let temp, tempMin, tempMax, pressure, seaLevel, grndLevel, humidity, tempKf : Double?
}

struct Cloudss : Decodable {
    let all : Int
}

struct Rainn: Codable {
    let the3H: Double?

    enum CodingKeys: String, CodingKey {
        case the3H = "3h"
    }
}

enum Pod: String, Codable {
    case d = "d"
    case n = "n"
}

enum MainEnum: String, Codable {
    case clear = "Clear"
    case clouds = "Clouds"
    case rain = "Rain"
}

ViewController below:

class ForecastViewController: UIViewController {


    @IBOutlet weak var testLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()


        guard let APIUrl = URL (string: "https://api.openweathermap.org/data/2.5/forecast?q=London&APPID=***APIKEY***&units=metric") else { return }
        //API KEY


    URLSession.shared.dataTask(with: APIUrl) { data, response, error in
        guard let data = data else { return }


        let decoderr = JSONDecoder()

        do {

            decoderr.keyDecodingStrategy = .convertFromSnakeCase
            decoderr.dateDecodingStrategy = .secondsSince1970

        let forecastData = try decoderr.decode(MyForecast.self, from: data)

        if let test = forecastData.list.first?.city.name { //using .first because Weather is stored in an array
            let description = test.description
            print(description)
            DispatchQueue.main.async {
                self.testLabel.text! = description
            }
        }

            else
                {
                    print("weather not found")
                }

        } catch {
            print(error.localizedDescription)
        }
        }.resume()

Your structs are were wrong before you edited the question.

The 5 day / 3 hour Forecast API of openweathermap.org sends a different JSON structure as the Current Weather Data.

You can create the structs very easy yourself:

  • Download the Data
  • Create a (JSON) string from the data
  • Copy the text
  • Open app.quicktype.io
  • Paste the text in the JSON text field on the left side
  • Add a suitable name for the root object.

quicktype.io creates the structs for you.

The forecast structs are (except Rain there are no optionals at all )

struct MyForecast : Decodable {
    let cod : String
    let message : Double
    let cnt : Int
    let list : [List]
    let city : City
}

struct Coordinate : Decodable {
    let lat, lon : Double
}

struct City : Decodable {
    let id, population : Int
    let name, country : String
    let coord : Coordinate
}

struct Main : Decodable {
    let temp, tempMin, tempMax : Double
    let seaLevel, grndLevel, tempKf: Double
    let pressure, humidity : Int
}

struct Wind : Decodable {
    let speed : Double
    let deg : Double
}

struct Weather : Decodable {
    let id : Int
    let icon : String
    let main : MainEnum
    let description: String
}

struct List : Decodable {
    let dt : Date
    let main : MainForecast
    let weather : [Weather]
    let clouds : Clouds
    let wind : Wind
    let sys : Sys
    let dtTxt : String
    let rain: Rain?
}

struct Sys : Decodable {
    let pod: Pod
}

struct MainForecast : Decodable {
    let temp, tempMin, tempMax, pressure, seaLevel, grndLevel, humidity, tempKf : Double
}

struct Clouds : Decodable {
    let all : Int
}

struct Rain: Codable {
    let the3H: Double?

    enum CodingKeys: String, CodingKey {
        case the3H = "3h"
    }
}

enum Pod: String, Codable {
    case d, n
}

enum MainEnum: String, Codable {
    case clear = "Clear"
    case clouds = "Clouds"
    case rain = "Rain"
}

To decode the structs you have to add date and key decoding strategies.

List and Weather are arrays

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .secondsSince1970
let forecastData = try decoder.decode(MyForecast.self, from: data)
if let test = forecastData.list.first?.weather.first? { //using .first because Weather is stored in an array
    let description = test.description
    print(description)
    DispatchQueue.main.async {
        self.testLabel.text! = description
    }
} else { print("weather not found") }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM