2 Replies
      Latest reply: Jan 13, 2017 8:56 AM by macnib RSS
      JayCo Level 1 Level 1 (5 points)

        Hi, I'm trying to parse some json data received and I'm struggling to understand how to pull out and assign the nested values. I can manage the first two levels but when it comes any further I struggle.

        I have an updateUI function which receives the data as String: AnyObject which works fine along with the first 2 levels of nesting.

        What I'm trying to parse is the "forecastday" "day" array so that I can update the UILabels.

         

        The current function is:

        
        func updateUI(weatherData: [String: AnyObject]) {
             
                let currentWeather = weatherData["current"]!
                let forecast = weatherData["forecast"]!
                let forecastDay = forecast["forecastday"] as AnyObject
             
                let day = forecastDay["day"] as Any
             
                print(day)
             
                DispatchQueue.main.async {
                    self.windDirectionLabel.text = (currentWeather["wind_dir"] as! String)
                }
        }
        

         

        My json data is:

         

        {
           "current":{
              "last_updated_epoch":1484042228,
              "last_updated":"2017-01-10 09:57",
              "temp_c":6.0,
              "temp_f":42.8,
              "is_day":1,
              "condition":{
                 "text":"Partly cloudy",
                 "icon":"//cdn.apixu.com/weather/64x64/day/116.png",
                 "code":1003
              },
              "wind_mph":23.0,
              "wind_kph":37.1,
              "wind_degree":290,
              "wind_dir":"WNW",
              "pressure_mb":1005.0,
              "pressure_in":30.2,
              "precip_mm":0.6,
              "precip_in":0.02,
              "humidity":93,
              "cloud":0,
              "feelslike_c":0.8,
              "feelslike_f":33.5
           },
           "forecast":{
              "forecastday":[
                 {
                    "date":"2017-01-10",
                    "date_epoch":1484006400,
                    "day":{
                       "maxtemp_c":8.1,
                       "maxtemp_f":46.6,
                       "mintemp_c":5.7,
                       "mintemp_f":42.3,
                       "avgtemp_c":5.9,
                       "avgtemp_f":42.6,
                       "maxwind_mph":22.4,
                       "maxwind_kph":36.0,
                       "totalprecip_mm":1.0,
                       "totalprecip_in":0.04,
                       "condition":{
                          "text":"Cloudy",
                          "icon":"//cdn.apixu.com/weather/64x64/day/119.png",
                          "code":1006
                       }
                    }
                    
                  }]
                  }
             }
        } // The rest ommitted to preseerve space :)
        

         

        Any help would be great.

        Thanks

        • Re: Swift 3 Parsing JSON nesting issue
          eskimo Apple Staff Apple Staff (5,995 points)

          I'm struggling to understand how to pull out and assign the nested values.

          Are you just trying to parse the JSON?  Or do you need to assign values within the JSON?  The latter is trickier, so I’m going to assume you’re interested in the former.

          With regards the former, the approach I usually use is to grind down each level of the JSON, casting values to the right type as I go.  For example:

          import Foundation
          
          let jsonData = try! JSONSerialization.data(withJSONObject: [
              "forecast": [
                  "forecastday": [
                      [
                          "day": [
                              "maxtemp_c": 1.2,
                              "maxtemp_f": 3.4
                          ]
                      ],
                      [
                          "day": [
                              "maxtemp_c": 5.6,
                              "maxtemp_f": 7.8
                          ]
                      ]
                  ]
              ]
          ], options: .prettyPrinted)
          let jsonRoot = try! JSONSerialization.jsonObject(with: jsonData)
          let rootDict = jsonRoot as! [String:Any]
          let forecast = rootDict["forecast"] as! [String:Any]
          let forecastdays = forecast["forecastday"] as! [[String:Any]]
          let forecastday0 = forecastdays[0]
          let day = forecastday0["day"] as! [String:AnyObject]
          let maxTemp = day["maxtemp_c"] as! Double
          print(maxTemp)
          

          Obviously this isn’t appropriate for production code (all those exclamation marks will crash if something goes wrong) but it should be enough to get you up’n’limping.


          Parsing JSON is the canonical ‘introduction to Swift’ question, and you’ll find as many opinions on this as you will Swift programmers.  Personally I lean towards the approach outlined in Working with JSON in Swift.  It’s a bit long-winded, but it’s really easy to understand.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

          • Re: Swift 3 Parsing JSON nesting issue
            macnib Level 1 Level 1 (10 points)

            This works for me in playgrounds.

             

            typealias JSONDictionary = [String: Dictionary<String, Any>]
            typealias JSONArray = [JSONDictionary]
            
            let jsonRoot = try! JSONSerialization.jsonObject(with: jsonData) as? JSONDictionary
            let forecasts = jsonRoot?["forecast"]?["forecastday"] as? JSONArray
            
            if let forecasts = forecasts {
            
              for forecast in forecasts {
                   let temp = forecast["day"]?["maxtemp_c"]
                   if let temp = temp {
                        print("Max Temp: \( temp )")
                   }
              }
            
            }
            
            

             

            minor edit: made types start with caps.