JsonDecoding fails with "Expected Array but found dictionary"

Hello CodeCrew,

after a two-day struggle and trying different approaches I came to the conclusion that I need to ask the community.

Setup

First of all, to be prepared, please clone my GitHub repo HERE

What does it do?

The app is simple and was made while following the tutorial of CWC “Full Stack iOS App”. It looks almost the same except that I use two API calls, one for the first ViewController and another for the second one.
Each API request delivers different data.

JSON structure

The JSON structure looks like this:

Problem

Every time I try to open the app, I get the error from JsonDecoder that it “Expected Array but found dictionary”.
I am very sure that I made something wrong with my structs. Although I used some Json to swift generators found online, it did not work.

Structs (I tried Codable and Decodable)

struct Root: Codable {
    let stations: [Station]
}

struct Station: Codable {
    let name: String
    let data: [Data]
}

struct Data: Codable {
    let auftraege, einstiege: Int?
    let wartezeit: String?
}

ViewController

First I declared an array of [Stations]

var stationsArray = [Stations]()

Then I call the service

HPService.services.fetchHalteplaetze()

Then I decode

let jsonDecoder = JSONDecoder()
stationsArray = try jsonDecoder.decode([Stations].self, from: stationsNewArray.data(using: .utf8)!)

And finally, I try to access the data and assign it to my TableViewCell

let cell = tableView.dequeueReusableCell(withIdentifier: "hpCell", for: indexPath) as! StationsTableViewCell
cell.hpName.text = stationsArray[indexPath.row].name
cell.auftraege.text = String((stationsArray[indexPath.row].data?.auftraege)!)
cell.einstiege.text = String((stationsArray[indexPath.row].data?.einstiege)!)
cell.wartezeit.text = stationsArray[indexPath.row].data?.wartezeit  
return cell

I’m really grateful for any help :slight_smile:
Cheers

On line 48 of ViewController.swift, change

stationsArray = try jsonDecoder.decode([Stations].self, from: stationsNewArray.data(using: .utf8)!)

to:

stationsArray = try jsonDecoder.decode(Root.self, from: stationsNewArray.data(using: .utf8)!)

Because the JSON you get back from the API call is not an array of Stations objects, but a dictionary with a stations key that contains an array of Stations objects.

And some advice: Rename your Stations struct to Station because it describes one set of station data and then you array reads better: [Station] instead of [Stations]

And don’t name a data type Data as that can conflict with the Foundation data type called Data. Name it something like StationData instead.

1 Like

Thanks for the feedback.
When I apply your suggestion I get a “Cannot assign value of type Root to type [Station]”, I tried this before but still stuck ^^

Ah, that’s because on line 19 you have:

var stationsArray = [Stations]()

So you should change line 48 to this:

let root = try jsonDecoder.decode(Root.self, from: stationsNewArray.data(using: .utf8)!)
stationsArray = root.stations ?? []
1 Like

Definitely the right way. Thank you so much :slight_smile:
In addition, I had to change the following lines too

cell.hpName.text = stationsArray[indexPath.row].name
cell.auftraege.text = String(stationsArray[indexPath.row].data[0].auftraege ?? 0)
cell.einstiege.text = String(stationsArray[indexPath.row].data[0].einstiege ?? 0)
cell.wartezeit.text = stationsArray[indexPath.row].data[0].wartezeit ?? "-"

Now it works perfectly fine and I say “Thank you” again
My next step is to implement a detailed view for each cell. This will be a nice challege fringersCrossed

Cheers