Search code examples
jsonswift

Swift JSON decoding error: Key not found for "id" in custom type AnimalGroup


I'm encountering a fatal error when trying to decode a JSON file into my custom Swift struct AnimalGroup. The error occurs when decoding the file classification.json, and it specifically complains about a missing "id" key even though the "id" key is present in the JSON.

Fatal error: Couldn't parse classification.json as AnimalGroup:
keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"id\", intValue: nil) (\"id\").", underlyingError: nil))

Here is my AnimalGroup struct:

struct AnimalGroup: Identifiable, Decodable, Hashable {
    let id: Int
    let code: Int
    let form: String?
    
    let name: String
    let shortName: String
    
    func hash(into hasher: inout Hasher) {
            hasher.combine(id)
            hasher.combine(name)
        }
}

This is how I load JSON data and return the decoded data:

var previewAnimal: Animal = load("animal.json")
var previewGroup: AnimalGroup = load("classification.json")


// Function that loads JSON data into a T type and returns the decoded data
func load<T: Decodable>(_ filename: String) -> T {
    let data: Data

    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
    }

    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }

    do {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase // Remember to add this line to avoid running into a crash
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

Here is a sample of my classification.json file:

{
    "group": [
        {
            "code": 3,
            "form": null,
            "id": 1,
            
            "name": "Vertebrates",
            "short_name": "CRS",
            
        },
        {
            "code": 4,
            "form": null,
            "id": 2,
            
            "name": "Mammal",
            "short_name": "CRS",
            
        },
        {
            "code": 5,
            "form": null,
            "id": 3,
            
            "name": "Reptile",
            "short_name": "CRS",
            
        },
        {
            "code": 6,
            "form": null,
            "id": 4,
            
            "name": "Avians",
            "short_name": "CRS",
            
        },
        {
            "code": 7,
            "form": null,
            "id": 5,
            
            "name": "Amphibians",
            "short_name": "CRS",
            
        }
        
    ]
}

My question: How can I handle the situation where a key like "id" is not missing from the JSON but Xcode is saying it's missing?

I used the Decodable protocol to decode my JSON into the AnimalGroup struct. The JSON includes the id field, so I expected the decoding to work. However, despite the id being present in the JSON, I'm still getting the keyNotFound error during decoding.

I was expecting the decoder to map the id key from the JSON to the AnimalGroup struct without any issues, but it results in a fatal error instead.


Solution

  • Your struct handles the fields of a single animal. Your JSON contains a dictionary with the key "group". The value for that key is an array of your AnimalGroup structs.

    You need an outer object that hosts an array of AnimalGroup objects ("AnimalGroupGroup"?)