Search code examples
jsonswiftdecodable

Different key type for the same json object in network response


My client has a service that returns Article objects. There is an id property that it is of type UInt64. In the same api when you ask for category articles you get a response with articles but the id is a String. There is no way at the moment that someone will change this stupid thing so I have to find a workaround to parse both answers. My model is something like this:

struct Article {
  let id: UInt64
  let categoryName: String?
}

extension Article: Decodable {
  private enum Keys: String, CodingKey {
    case id
    case categoryName
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: Keys.self)

    id = try container.decode(UInt64.self, forKey: Keys.id)
    categoryName = try container.decodeIfPresent(String.self, forKey: Keys.categoryName)
}

How to check the type of Keys.id and use the correct method to decode? I have to use both

   id = try container.decode(UInt64.self, forKey: Keys.id)
   id = try container.decode(String.self, forKey: Keys.id) 

to parse my objects correctly in both cases. Thanks in advance


Solution

  • If you want workaround, then below answer does that. I don't know if this is also a possible good way to do this. Looking forward to improve this answer. Suggestions are welcome.

    extension Article: Decodable {
    private enum Keys: String, CodingKey {
        case id
        case categoryName
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Keys.self)
        // check if id is of type UInt64
        do{
            id = try container.decodeIfPresent(UInt64.self, forKey: Keys.id) ?? 0
    
        }catch{
            // if id is of String type convert it to UInt64
            // do catch can be used here too
            let str = try container.decodeIfPresent(String.self, forKey: Keys.id)
            id = UInt64(str ?? "0") ?? 0
    
        }
        categoryName = try container.decode(String.self, forKey: Keys.categoryName)
    
    }
    }