MasterDNE
MasterDNE

Reputation: 67

How to pull out data from different levels of JSON objects

I have a JSON object that looks like this:

{
        geometry =     {
            location =         {
                lat = "51.5194133";
                lng = "-0.1269566";
            };
        };
        id = ad6aaec7b7b0fa2c97a127c24845d76135e760ae;
        "place_id" = ChIJB9OTMDIbdkgRp0JWbQGZsS8;
        reference = "CmRRAAAAiC-ErdlAvz74Drejj2mAAh6Plr46e889a3Uv6CrRXFqNtVatoFsOTarDH0KU8KCkWoN--QGv01RSjLBZblbrAHNPGDVdiXikedid0vKMVM_LQtXstrSQFt4s-Z-Wi-1AEhDJRWc9bdWpKHPPOrt7QGTqGhSJgMPENn_wSGbprGYLv52csv5BtQ";
    }

I was wondering how you can extract the information at different levels. For example, the location object is an object within the geometry object and I want to extract lat from there how can I do this?

I can print out the location object like:

let setOne = jsonResult["results"]! as! NSArray
            let y = setOne[0] as? [String: AnyObject]
            print(y!)
            print((y!["geometry"]!["location"]!)!["lat"])

but when I try to do:

print((y!["geometry"]!["location"]!)!["lat"])

it gives me the error:

Type 'Any' has no subscript members

Upvotes: 0

Views: 341

Answers (3)

thedansaps
thedansaps

Reputation: 611

Considering you have an array of data as results (jsonResult), start accessing your array and identify the type of each parameter.

if let resultArray = jsonResult["results"] as? NSArray {
        for aResult in resultArray {

            //using "if let" simply means, in this case, if "geometry" is indeed a dictionary it will execute the statement/s inside this "if" block.
            if let geometry = aResult["geometry"] as? NSDictionary {
                if let location = geometry["location"] as? NSDictionary {
                    if let latValue = location["lat"] as? String {
                        //This will only be executed if the value is String
                        print(latValue)
                    }

                    if let lngValue = location["lng"] as? String {
                        print(lngValue)
                    }
                }
            }
        }
    }

Upvotes: 0

Mike Taverne
Mike Taverne

Reputation: 9352

Perhaps the easiest way to do this is to use JSONDecoder to decode your JSON directly into structs.

You first need to define structs that match your JSON structure, like so:

struct Place: Codable {
    let geometry: Geometry
    let id: String
    let place_id: String
    let reference: String
}
struct Geometry: Codable {
    let location: Location
}
struct Location: Codable {
    let lat: String
    let lng: String
}

Now, assuming that jsonResult["results"] is in fact an NSArray, you first need to convert it to Data and then use the JSONDecoder to decode the JSON into the structs:

if let data = try? JSONSerialization.data(withJSONObject: jsonResult["results"], options: []) {
    if let places = try? JSONDecoder().decode(Array<Place>.self, from: data) {
        print(places[0].geometry.location.lat) //prints "51.5194133"
    }
}

The advantage of this approach is that you write much less code to do the actual decoding.

Note that if any of the JSON elements might be missing, you should declare the corresponding struct let property as an optional. For example, if reference might not always be present, you would code it as:

let reference: String?

Anything that is not optional must be present in the JSON, or the decode will fail. This is why you want to use try? when decoding, so that your app does not crash.

Upvotes: 2

AaoIi
AaoIi

Reputation: 8396

Lets say you have an array of results data, you don't need to use NSArray since you are using Swift so here you can start.

In Swift you need to specify the type, And also try not to use AnyObject unless you really need it, Always provide the type and thats what the error is saying:

// make sure that results exist 
if let resultArray = jsonResult["results"] as? [Dictionary<String,Any>] {
 // loop through the result array
 for object in resultArray {
   // get geometry from the object in the array
   if let geometry = object["geometry"] as? Dictionary<String,Any>{
     // make sure it has location
     if let location = geometry["location"] as? Dictionary<String,Any>{

       // now get the lat or lng safely.
       let lat = location["lat"] as? String ?? "0.0" 
       let lng = location["lng"] as? String ?? "0.0"   

     }
   } 
  }
}

Note: Never use force-unwrap ! because your app will crash if something went wrong or that specific object is not found, make sure to use guard let or if let at least, although this is manual parsing you can look into Swift 4 new Encoding, Decoding and Serialization in Swift 4.

Upvotes: 0

Related Questions