简体   繁体   中英

Correctly parsing through a JSON array with multiple objects using Alamofire and SwiftyJSON

I am able to print out the response. However I am unable to loop through the array and initialise it. This is how I am requesting the data:

Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default).responseJSON { (response) in

    print(response.value!)

    guard let data = response.data else { return }
    let json = JSON(data: data)

    for r in json.arrayValue {
        let person = Person(json: r)
    }
}

I have added breakpoints but I am unable to understand as to why the loop does not happen?

Update:

response.value! example:

{
  "pagination": {
    "object_count": 1,
    "page_number": 1
  },
  "attendees": [
    {
      "id": "818060297",
      "quantity": 1,
      "profile": {
        "first_name": "John",
        "last_name": "Doe",
        "company": "Apple",
        "name": "John Doe",
        "email": "john_doe@testmail.com",
        "job_title": "CEO"
      },
      "status": "Attending"
    }
  ]
}

Update 2

Here is my AttendeesProfile class:

class AttendeesProfile {

    var name: String?

    init(json: JSON) {
        self.name = json["name"].stringValue
    }

}

Now I am not sure how to get the Attendee class to work properly:

class Attendees {

    var attendees = [String]()
    var attendeesProfile: AttendeesProfile!

    init(json: JSON) {
        // Something here
    }
}

I suspect your guard statement stopping your code flow before you loop through your array : guard let data = response.data else { return }

With this line, you are saying if data has something then the code flow can continue. If not, please stop it and return. So, you never reach your loop statement.

Are you sure that your response.data has something and is different from nil ? Your print(response.value!) shows something ?

You have to cast your response data. You can not walk on an unknown array I suggest you to use ObjectMapper library. It can parse your data to your willing model and if received data is nil or is not your wanted model you can find out easily. Do not forget to print response.data to ensure what data exactly is. https://github.com/Hearst-DD/ObjectMapper

You could implement a couple of structs conforming to Decodable , which among other things would give you the benefit of not relying on SwiftyJSON for your code to work.

Going off from the JSON data that you have provided, consider the following three simple structs:

struct AttendeeArray: Decodable {
   let attendees: [Attendee]
}

struct Attendee: Decodable {
   let status: String
   let profile: AttendeeProfile
}

struct AttendeeProfile: Decodable {
   let name: String
   let age: Int
}

Each struct simply contains the variables that you have defined in your JSON object.

Using the JSONDecoder you will now be able to decode your JSON data as simple as calling:

do { 
   let array = try JSONDecoder().decode(AttendeeArray.self, from: data)
   // do whatever with your attendees array
} catch {
   // handle error if parsing fails
   print(error)
}

I created a simple Playground where you can test this by adding the code bolow along with the Decodable structs above:

import Foundation

func decodeAttendees(json: String) -> AttendeeArray? {
   guard let data = json.data(using: .utf8) else { return nil }
   do {
       return try JSONDecoder().decode(AttendeeArray.self, from: data)
   } catch {
       print("Error: \(error)")
       return nil
   }
}

let json = """
{
   "attendees": [
       {
           "status": "attending",
           "profile": {
               "name": "Joe",
               "age": 22
           }
       },
       {
           "status": "not attending",
           "profile": {
               "name": "Bob",
               "age": 44
           }
       }
   ],
   "servlet": {
       "servlet-name": "cofaxCDS",
       "servlet-class": "org.cofax.cds.CDSServlet"
   }
}
"""

let arr = decodeAttendees(json: json)
arr?.attendees[0].profile.name //"Joe"
arr?.attendees[1].status //"not attending"

Now, for your current Alamofire completion handler, I'm guessing that it would be as simple to modify it to be something along the lines of:

Alamofire.request(url, method: .get, parameters: nil, encoding:   URLEncoding.default).responseJSON { (response) in
   guard let data = response.data else { return //remember to error if data is nil }
   do {
      let array = JSONDecoder().decode(AttendeesArray.self, from: data)
      //update your UI to show the array, or pass it on to a completion hanldler
   } catch {
      //handle errors
   }
}

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