How can I identify and group data in a JSON file?

I have a large JSON file with a top level array:

[ { "tag" : "AF", "geopoliticalarea" : "Afghanistan", "travel_transportation" : etc... } ]

The JSON file is a list of every country along with info for each country. You can see it here if you'd like:

https://cadatacatalog.state.gov/dataset/4a387c35-29cb-4902-b91d-3da0dc02e4b2/resource/299b3b67-3c09-46a3-9eb7-9d0086581bcb/download/countrytravelinfo.json

I've set up a struct for the JSON info:

struct ReceivedAPIData: Codable {

    let tag: String
    let geopoliticalarea: String
    let travel_transportation: String // with html
    let health: String // with html
    let destination_description: String // with html
    let iso_code: String
    let travel_embassyAndConsulate: String // with html
    let last_update_date: String

}

(Initially I wanted to use an API call but if it's easier to save all of the data locally I'm not opposed to that).

What I'm having trouble with is identifying/grouping/and pulling out all the the above info for one country at a time.

The entire file is in an array but I don't know how to access each country. If I look at the link with Firefox I can see that each country is assigned a number, 0 - 210, but maybe that's just Firefox helping out? I don't see any numbers like that in the raw JSON.

Here's my API call func:

        guard let apiUrl = URL(string: "https://cadatacatalog.state.gov/dataset/4a387c35-29cb-4902-b91d-3da0dc02e4b2/resource/299b3b67-3c09-46a3-9eb7-9d0086581bcb/download/countrytravelinfo.json")
        else {
            print("Error: cannot create URL.")
            return
        }
        let session = URLSession(configuration: .default)

        let task = session.dataTask(with: apiUrl) { [self] data, response, error in
            let decoder = JSONDecoder()
            guard let safeData = data else {
                print("Couldn't get data back from url.");
                return
            }


            guard let safeDecodeData = try? decoder.decode([ReceivedAPIData].self, from: safeData) else {
                fatalError("Couldn't decode data.")}

            self.delegate?.didUpdateAPIResults(returnedResult: safeDecodeData)



            let jsonStringData = data?.prettyPrintedJSONString
            print("Pretty JSON from server ----- \(String(describing: jsonStringData))")
            //     Use this to remove the HTML from the string data later       safeDecodeData.map -> { ReceivedAPIData in
            //
            //            }
            if let e = error {
                print(e.localizedDescription)
            }
            callCompletionHandler(safeDecodeData)
        }
        task.resume()
    }
}

My goal for this app is to search by country name in a text field and have only the info for that county displayed.

I've googled around about top level arrays and nested JSON info but can't figure out what my next step is.

Thanks.

I've googled around about top level arrays and nested JSON info but can't figure out what my next step is.

Seems your Googling ability is not good enough. You can find bunch of articles searching with swift searching one element in an array.


You should show your code enough to test instantly, especially when you show some code in a method, you should show at least the whole method. Partially shown code might often lead to an unexpected and unusable answers.

I needed to guess what was the header of the method and some other things. And I assumed something as follows:

    @IBOutlet weak var targetAreaField: UITextField!
    
    func apiCall(callCompletionHandler: @escaping([ReceivedAPIData])->Void) {
        guard let apiUrl = URL(string: "https://cadatacatalog.state.gov/dataset/4a387c35-29cb-4902-b91d-3da0dc02e4b2/resource/299b3b67-3c09-46a3-9eb7-9d0086581bcb/download/countrytravelinfo.json")
        else {
            print("Error: cannot create URL.")
            return
        }
        let session = URLSession(configuration: .default)
        
        let task = session.dataTask(with: apiUrl) { [self] data, response, error in
            if let error = error {
                print(error)
                return
            }
            
            guard let safeData = data else {
                print("Couldn't get data back from url.");
                return
            }
            
            let decoder = JSONDecoder()
            let safeDecodeData: [ReceivedAPIData]
            do {
                safeDecodeData = try decoder.decode([ReceivedAPIData].self, from: safeData)
            } catch {
                fatalError("Couldn't decode data.")
            }
            
            self.delegate?.didUpdateAPIResults(returnedResult: safeDecodeData)
            
            //let jsonStringData = data?.prettyPrintedJSONString ?? "**data cannot be prity-printed**"
            //print("Pretty JSON from server ----- \(jsonStringData)")
            //...
            callCompletionHandler(safeDecodeData)
        }
        task.resume()
    }

(I have corrected some bad practices in your code, but there is still one unrecommended thing: You should better call completion handler in case of errors. But that is another issue.)

When I searched with swift searching one element in an array, this Apple' doc showed in the top page:

first(where:) | Apple Developer Documentation.

You can use it like this:

        guard let targetArea = targetAreaField.text, !targetArea.isEmpty else {
            return
        }
        apiCall { result in
            //print(result.count)
            guard let targetResult = result.first(where: {$0.geopoliticalarea == targetArea}) else {
                print("NO data for \(targetArea)")
                return
            }
            print(targetResult)
            //...
        }

Thanks, I had no idea this method existed, I'll play around with it.

How can I identify and group data in a JSON file?
 
 
Q