Crazy Developer
Crazy Developer

Reputation: 3464

How to parse data with Codable in json has key but value not match with enum any case

I have created a model as per below with one custom enum for gender

enum Gender :String, Codable {
    case male = "Male"
    case female = "Female"
}

class Person : Codable {
    var name : String?
    var gender : Gender?

    convenience init(name : String, gender : Gender) {
        self.init()
        self.name = name
        self.gender = gender
    }

    enum CodingKeys: String, CodingKey {

        case name    = "name"
        case gender  = "gender"

    }
}

Below is my json data from the API

[
  {
    "name": "name1",
    "gender": "Male"
  },
  {
    "name": "name2",
    "gender": "Male"
  }
]

in the success block of response if I am parsing data with below code it is working fine

  do {

        let list = try JSONDecoder().decode([Person].self, from: data)

        print("list \(list)")

    }
    catch {
        print("error \(error)")
    }

Now Problem is when in the response api if gender is provided in all lower case like "gender": "male" then the parsing is not working and give below error.

error dataCorrupted(Swift.DecodingError.Context(codingPath: 
[_JSONKey(stringValue: "Index 1", intValue: 1), CodingKeys(stringValue:
"gender", intValue: nil)], debugDescription: "Cannot initialize Gender from
 invalid String value male", underlyingError: nil))

I want any one solution from below

One more thing I don't want to override encoder and decoder method as I have many parameter in my actual project. I am already aware of that.

Upvotes: 3

Views: 678

Answers (1)

Ashley Mills
Ashley Mills

Reputation: 53131

You can do this by overriding the init(from decoder:) for Person

enum Gender :String, Codable {
    case male, female // Note that I removed the uppercase string values 
}

struct Person : Codable {
    var name : String?
    var gender : Gender?

    enum CodingKeys: String, CodingKey {
        case name, gender // No need for string values if they match the enum cases
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decodeIfPresent(String.self, forKey: .name) // decodeIfPresent as name is optional 

        let genderString = try container.decode(String.self, forKey: .gender)
        gender = Gender(rawValue: genderString.lowercased()) // This is why I removed the uppercase String values
    }
}

Upvotes: 2

Related Questions