kakubei
kakubei

Reputation: 5400

Swift 4 JSON decoder with nested data

This is the data structure (updated):

    {
  "date": "2018-10-18",
  "time_of_day": "16:00",
  "request_time": "2018-10-18T16:00:27+01:00",
  "station_name": "London Waterloo",
  "station_code": "WAT",
  "arrivals": {
    "all": [
      {
        "mode": "train",
        "service": "24673605",
        "train_uid": "W12378",
        "platform": "10",
        "operator": "SW",
        "operator_name": "South Western Railway",
        "aimed_departure_time": null,
        "aimed_arrival_time": "05:18",
        "aimed_pass_time": null,
        "origin_name": "Guildford",
        "destination_name": "London Waterloo",
        "source": "ATOC",
        "category": "OO",
        "service_timetable": {
          "id": "https://transportapi.com/v3/uk/train/service/train_uid:W12378/2018-10-18/timetable.json?app_id=80b56d0a&app_key=b44a5870830959a7a961fdbb65f9dc13"
        }
      },
      {
        "mode": "train",
        "service": "24671505",
        "train_uid": "W14110",
        "platform": "1",
        "operator": "SW",
        "operator_name": "South Western Railway",
        "aimed_departure_time": null,
        "aimed_arrival_time": "05:35",
        "aimed_pass_time": null,
        "origin_name": "Twickenham",
        "destination_name": "London Waterloo",
        "source": "ATOC",
        "category": "OO",
        "service_timetable": {
          "id": "https://transportapi.com/v3/uk/train/service/train_uid:W14110/2018-10-18/timetable.json?app_id=80b56d0a&app_key=b44a5870830959a7a961fdbb65f9dc13"
        }
      },
      {
        "mode": "train",
        "service": "24671105",
        "train_uid": "W14764",
        "platform": "15",
        "operator": "SW",
        "operator_name": "South Western Railway",
        "aimed_departure_time": null,
        "aimed_arrival_time": "05:41",
        "aimed_pass_time": null,
        "origin_name": "Staines",
        "destination_name": "London Waterloo",
        "source": "ATOC",
        "category": "OO",
        "service_timetable": {
          "id": "https://transportapi.com/v3/uk/train/service/train_uid:W14764/2018-10-18/timetable.json?app_id=80b56d0a&app_key=b44a5870830959a7a961fdbb65f9dc13"
        }
      }
    ]
  }
}

I've tried the answer here: Expected to decode Array<Any> but found a dictionary instead

But can't quite get it to work. I keep getting and error on this line:

let root = try JSONDecoder().decode(Root.self, from: data)

My models (updated):

 struct Root: Decodable {
    let arrivals: Arrivals
}

struct Arrivals: Decodable {
    let all: All
}

struct All: Decodable {
    let trains: [Train]
}

Error:

▿ DecodingError
  ▿ typeMismatch : 2 elements
    - .0 : Swift.Dictionary<Swift.String, Any>
    ▿ .1 : Context
      ▿ codingPath : 2 elements
        - 0 : CodingKeys(stringValue: "arrivals", intValue: nil)
        - 1 : CodingKeys(stringValue: "all", intValue: nil)
      - debugDescription : "Expected to decode Dictionary<String, Any> but found an array instead."
      - underlyingError : nil

Upvotes: 1

Views: 446

Answers (1)

matt
matt

Reputation: 534950

"I've tried the answer here: Expected to decode Array but found a dictionary instead

Yes, but your error message is just the opposite. Taking a moment to read the error message, we can see that it is perfectly clear and obviously right.

In your Arrivals struct, you are saying

let all: All

But the value of the "all" key in the JSON is an array! So the type here must be an array of something. In particular it should be an array of trains.

let all: [Train]

And now you can delete your All struct, which was never right.

Example (running against the JSON you showed):

struct Root: Decodable {
    let arrivals: Arrivals
}
struct Arrivals: Decodable {
    let all: [Train]
}
struct Train: Decodable {
}
let data = json.data(using: .utf8)!
if let root = try? JSONDecoder().decode(Root.self, from: data) {
    print("got", String(root.arrivals.all.count), "trains")
}
// "got 3 trains"

Upvotes: 2

Related Questions