Aovib
Aovib

Reputation: 27

How to parse JSON when the object contains an array of other objects?

I've been trying to parse a JSON in Swift where the object contains an array of other objects. Like this:

{
  "people": [
    {
      "name": "Life Sciences",
      "id": "4343435",

      "children" : [
        {
          "name": "name1",
          "id" : "5344444",
        },

        {
          "name": "name2",
          "id" : "5134343",
        },
      .....

I need to be able to access the name and id properties, but I can't seem to figure out what I'm doing wrong with my code below. My JSON file contains all the necessary data, yet, I keep getting the "unexpectedly found nil when unwrapping optional" error when I try to loop over the children array. Before that line the JSON is parsed correctly and works.

let loadURL = "https:// ....."
var people = [Person]()

func getPersonData() {
    let request = URLRequest(url: URL(string: loadURL)!)
    let urlSession = URLSession.shared
    let task = urlSession.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
        if let error = error {
            print(error)
            return
        }
        // Parse JSON data
        if let data = data {
            self.people = self.parseJsonData(data)
            OperationQueue.main.addOperation{() -> Void in
                self.tableView.reloadData()
            }
        }
    })
    task.resume()
}

func parseJsonData(_ data: Data) -> [Person] {
    var people = [Person]()

    do {
        let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary

        // Parse JSON data
        let jsonPeople = jsonResult?["people"] as! [AnyObject]
        for jsonPerson in jsonPeople {
            let person = Person()
            person.name = jsonPerson["name"] as! String
            person.id = jsonPerson["id"] as! String

            //ERROR//: "unexpectedly found nil when unwrapping optional..."
            let jsonChildren = jsonResult?["children"] as! [AnyObject]
            for jsonChild in jsonChildren {
                let child = Child()
                child.name = jsonEntrance["name"] as! String
                child.age = jsonEntrance["age"] as! Int

                person.children.append(child)
            }

            people.append(person)
        }
    } catch {
        print(error)
    }
    return people
}

Upvotes: 0

Views: 958

Answers (4)

vadian
vadian

Reputation: 285082

First of all, the JSON does not represent the actual JSON in the code.

Second of all, never ever use NSDictionary in Swift unless you have no choice.

Third of all, cast an JSON array containing dictionaries to [[String:Any]], never to [Any(Object)]

Fourth of all, a JSON dictionary in Swift 3 is [String:Any]

Fifth of all, use optional bindings to avoid runtime errors (crashes)

func parseJsonData(_ data: Data) -> [Person] {
  var people = [Person]()

  do {
    let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String:Any]

    // Parse JSON data
    if let jsonPeople = jsonResult["people"] as? [[String:Any]] {
      for jsonPerson in jsonPeople {
        let person = Person()
        person.name = jsonPerson["name"] as! String
        person.id = jsonPerson["id"] as! String

        // children is a key of a person not of the root object !
        if let jsonChildren = jsonPerson["children"] as? [[String:Any]] {
          for jsonChild in jsonChildren {
            let child = Child()
            child.name = jsonChild["name"] as! String
            child.age = jsonChild["age"] as! Int

            person.children.append(child)
          }
        }

        people.append(person)
      }
    }
  } catch {
    print(error)
  }
  return people
}

PS: You will get another error undefined identifier because jsonEntrance in the child loop in your code does not exist and children is a key of people not of the root object.

Upvotes: 0

A K M Saleh Sultan
A K M Saleh Sultan

Reputation: 2403

Your code looks good, but the problem is that you are search for wrong dictionary. Your 'jsonResult' key doesn't have a key for 'children'. But your 'jsonPerson' object has a 'children' key. Replace your below line of code -

let jsonChildren = jsonResult?["children"] as! [AnyObject]

Replace this line with this one -

            let jsonChildren = jsonPerson?["children"] as! [AnyObject]

Upvotes: 0

LarsJK
LarsJK

Reputation: 2196

You made an error here:

let jsonChildren = jsonResult?["children"] as! [AnyObject]

Should be:

let jsonChildren = jsonPerson["children"] as! [AnyObject]

Upvotes: 1

Liubo
Liubo

Reputation: 702

Probably, your JSON data on some point has no "children" value, try to avoid force casting to [AnyObject]. You may try to change it in this way:

if let result = jsonResult, let jsonChildren = result["children"] as? [AnyObject] {
      for jsonChild in jsonChildren {
           let child = Child()
           child.name = jsonEntrance["name"] as! String
           child.age = jsonEntrance["age"] as! Int

           person.children.append(child)
      }
}

Also, you may try use SwiftyJSON which will help you to do your json data processing much more easier.

Upvotes: 0

Related Questions