Reputation: 413
This is my first time taking a shot at Codable/Decodable and i would like to decode a JSON. I am attempting to access the "name" and "description" keys within the events array. Below is a snippet of the JSON - im getting this error within my code
"Expected to decode Dictionary but found an array instead."
"pagination": {
"page_number": 1,
"page_size": 50,
"continuation": "eyJwYWdlIjogMn0",
"has_more_items": true
},
"events": [
{
"name": {
"text": "Genesis Part 4",
"html": "Genesis Part 4"
},
"description": {
"text": "Wednesday, June 6-June 27, 2018\n12:00-2:15 PM, Eastern Time\n\u00a0\nCOED\n\u00a0\nBible Study Leader:\u00a0Nicki Cornett\n\u00a0\nContact:[email protected]\n\u00a0\nGenesis Part 4 -\u00a0Wrestling with God - A Study on Isaac, Jacob, and Esau- Precept Workbook (NASB)\n\u00a0\nGod renews His covenant promise with Abraham through Isaac and Jacob.
Here is how i went about decoding - (NOTE - "description" is not here yet because i having an issue working into the events array to access name & descritption
struct Eventbrite: Decodable {
private enum CodingKeys : String, CodingKey { case events = "events", name = "name"}
let events: [String:[String]]
let name: [String:[String]]
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {return}
do {
let eventbriteData = try JSONDecoder().decode(Eventbrite.self, from: data)
print(eventbriteData.name)
Upvotes: 0
Views: 306
Reputation: 1826
Right so Decodable is actually pretty smart in that you really don't need to write any code to do the decoding yourself. You just have to make sure you match the JSON structure (and make structs that also conform to Decodable for any nested objects). In other words, instead of having variables as dictionaries, make them their own Decodable struct.
So for example:
struct EventBrite: Decodable {
let pagination: Pagination
let events: [Event]
}
struct Pagination: Decodable {
let page_number: Int
let page_size: Int
let continuation: String
let has_more_items: Bool
}
struct Event: Decodable {
let name: EventName
let description: EventDescription
}
struct EventName: Decodable {
let name: String
let html: String
}
etc...
Something else that's important here is if a key or property is not guaranteed to be returned (like let's say that the EventName
doesn't always have an html value that comes back from the server you can easily just mark that value as optional. So something like:
struct EventName: Decodable {
let name: String
let html: String?
}
Another side note, you actually messed up your dictionary type declarations. You'll notice that event
is actually of type [String: [String: String]]
since the key is a string and the values seem to always be dictionary. And name is [String: String]
. Which is not what you had them down as in your original question.
When the values can be different like with pagination you'll want to do something like [String: Any]
so just be careful about that.
HOWEVER The approach I suggested I think is better than having properties be dictionaries. For one you don't have to worry about declaring the type of dictionary it is (which you made some small errors on). But more importantly when each dictionary just becomes its own clearly defined struct and you don't have to worry about remembering or looking up the keys. Dot syntax/auto complete will automatically tell you what there can be! (And no casting when your value is of type Any
or AnyObject
!)
Also definitely use structs for all these as I once benchmarked performance and measured structs efficiency on the order of magnitude of millions of times more efficient than classes. Just a FYI.
Upvotes: 2
Reputation: 285039
name
is clearly not in the scope of pagination
and events
(note the {}
) and is a regular [String:String]
dictionary which can be decoded into another struct.
Decode this (as description
is incomplete I left it out), you don't need CodingKeys
:
struct Eventbrite: Decodable {
let events: [Event]
}
struct Event: Decodable {
let name: Name
// let description: [String:String]
}
struct Name : Decodable {
let text, html : String
}
Upvotes: 2