Search code examples
jsonswiftenumsdecodablecodingkey

How can I make Swift CodingKeys for JSON data with non-unique keys?


I am using the Unsplash API and it gives this response (which is just a snapshot)

    "exif": {
        "name": "Canon, EOS 6D"
    },
    "location": {
        "name": "Antelope Canyon, United States",
    },
    "user": {
        "name": "Joe Gardner"
    }

I want to parse this into a Swift Decodable structure (using JSONDecocder):

struct WallpaperResponse: Decodable {
    
    var location: String?
    var camera: String?
    var username: String

    enum CodingKeys: String, CodingKey {
        case locationName = "name"
        case cameraName = "name"
        case username = "name"
        
    }
    
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // Decode location
        let locationContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .location)
        location = try locationContainer.decodeIfPresent(String.self, forKey: .locationName)
        // ...
        
        // Decode camera details
        let exifContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .exif)
        camera = try exifContainer.decode(String.self, forKey: .cameraName)
        
        // Decode user data
        let userContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .user)
        username = try userContainer.decode(String.self, forKey: .username)
    }
    
}

The problem is that the CodingKeys cannot have duplicate names, but my API does give duplicate names. Is there a way to parse duplicate names in JSON using a single CodingKeys without making a CodingKeys2 or similar approach?

Thanks


Solution

  • You can do it in this way:

    struct WallpaperResponse: Decodable {
        var location: Location
        var exif: Exif
        var user: User
    
        struct Location: Decodable {
            var name: String
        }
    
        struct Exif: Decodable {
            var name: String
        }
    
        struct User: Decodable {
            var name: String
        }
    }