0

I have looked through other threads regarding trying to parse JSON data where a JSON array has no name. From what I have found you need to use a unkeyedContainer but I'm not entirely sure from the examples I have seen how this works with the data model.

Below is a snippet of data from open charge api:

[
{
"IsRecentlyVerified": false,
"ID": 136888,
"UUID": "254B0B07-E7FC-4B4B-A37C-899BCB9D7261",
"DataProviderID": 18,
"DataProvidersReference": "0a9fdbb17feb6ccb7ec405cfb85222c4",
"OperatorID": 3,
"UsageTypeID": 1,
"AddressInfo": {
"ID": 137234,
"Title": "Ballee Road Park & Share",
"AddressLine1": "Ballee Road",
"Town": "Ballymena",
"Postcode": "BT42 2HD",
"CountryID": 1,
"Latitude": 54.844648,
"Longitude": -6.273606,
"AccessComments": "Ballee Road Park and Share, Ballymena",
"RelatedURL": "http://pod-point.com",
"Distance": 3.81818421833416,
"DistanceUnit": 2
},
"Connections": [
{
"ID": 191571,
"ConnectionTypeID": 25,
"Reference": "1",
"StatusTypeID": 50,
"LevelID": 2,
"Amps": 32,
"Voltage": 400,
"PowerKW": 22,
"CurrentTypeID": 20
},

It looks to me that the first [ and { have no attribute names which I belive is creating the error in xcode: "Error!: typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))"

Here is my data model:

import Foundation

struct PublicCharger: Decodable {
    let AddressInfo: [AddressInfo]

}

Here is my code:

//Find public chargers from local coordinates
    func findPublicChargers(lat: Double, long: Double) {
        //Use apiurl to pull all charge points that are currently in that area by adding lat and long into the api call &latitude=***&longitude=*****
        let apiurl = "https://api.openchargemap.io/v3/poi/?output=json&countrycode=UK&maxresults=100&compact=true&verbose=false"
        let urlString = "\(apiurl)&latitude=\(lat)&longitude=\(long)"
        //print(urlString)
        performRequest(urlString: urlString)
        
    }
    
    //Perform API Request - (London App Brewry code)
    //Create the custom url
    
    func performRequest(urlString: String) {
        if let url = URL(string: urlString) {
            //print("Called")
            //Create a URL Session
            let session = URLSession(configuration: .default)
            //Give the session a task
            let task = session.dataTask(with: url) { (data, response, error) in
                if error != nil {
                    print(error!)
                    return
                }
                if let safeData = data {
                    //let dataString = String(data: safeData, encoding: .utf8)
                    //print(dataString)
                    self.parseJSON(data: safeData)
                    print("Data: \(safeData)")
                }
            }
            //Start the task
            task.resume()
        }
    }
    
    func parseJSON(data: Data){
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode(PublicCharger.self, from: data)
            print("Data: \(decodedData.AddressInfo[0].Title)")
        } catch {
            print("Error!: \(error)")
        }
    }
    
struct AddressInfo: Decodable {
    let Title: String
}

I have seen that in the data model you would need to include an unkeyed container element. I'm just not sure how this should be carried out in the data model. Any light on this would be much appreciated.

1
  • This JSON looks fine. It's an array of objects. Paste the JSON into app.quicktype.io and it'll write the decoder for you. You'll just need to decode something like [Record].self rather than Record.self. (But quicktype will do that for you.) Commented Mar 4, 2021 at 16:48

1 Answer 1

1

Try to change your PublicCharger data model to

struct PublicCharger: Decodable {
    let AddressInfo: [AddressInfo]
}

And your parseJSON function to

func parseJSON(data: Data){
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode([PublicCharger].self, from: data)
            if !decodedData.isEmpty {
                print("Data: \(decodedData[0].AddressInfo[0].Title)")
            } else {
                print("Empty result!")
            }
        } catch {
            print("Error!: \(error)")
        }
    }
Sign up to request clarification or add additional context in comments.

5 Comments

Ah I left the line let stringArray: [String] in the data model when I was trying to troubleshoot and your right it shouldn't be there, but still getting the same error. I have updated the above code to remove it.
Still, you should decode your PublicCharger array with try decoder.decode([PublicCharger].self, from: data)
ah I see what you mean, sorry I didn't see the [PublicCharger] before. Tried this and now it won't compile with error: Value of type '[PublicCharger]' has no member 'AddressInfo'.
It's an error in the print method, since you are now expecting an array you should access your first PublicCharger item. Check the edited answer.
That's brilliant thank you! The thing that threw me the most was having the [0] at the end of decodedData[0] which makes a lot of sense now!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.