Coding while Loading
Coding while Loading

Reputation: 413

decoding JSON array or dictionary error swift

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

Answers (2)

gadu
gadu

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

vadian
vadian

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

Related Questions