arraysjsonswiftdecodable

Decoding an array of objects with nested heterogeneous child objects


An example of the response from the server is below.

The list consists of elements that have heterogeneous substructures in the info fields. Each of them contains 3 fields with the same types, but they have different keys.

I don't know how to decode this, I haven't encountered such a problem so far. I can't find an example on the Internet that fits this case.

I wanted to decode the enum type at the beginning and select the appropriate info structure based on it, but it doesn't work.

{
  "data":[
    {
      "type":"league",
      "info":{
        "name":"NBA",
        "sport":"Basketball",
        "website":"https://nba.com/"
      }
    },
    {
      "type":"player",
      "info":{
        "name":"Kawhi Leonard",
        "position":"Small Forward",
        "picture":"https://i.ibb.co/b5sGk6L/40a233a203be2a30e6d50501a73d3a0a8ccc131fv2-128.jpg"
      }
    },
    {
      "type":"team",
      "info":{
        "name":"Los Angeles Clippers",
        "state":"California",
        "logo":"https://logos-download.com/wp-content/uploads/2016/04/LA_Clippers_logo_logotype_emblem.png"
      }
    }
  ]
}

Solution

  • Your code on pastebin is too complicated, I mean this

    let jsonString = """
    {
      "data":[
        {
          "type":"league",
          "info":{
            "name":"NBA",
            "sport":"Basketball",
            "website":"https://nba.com/"
          }
        },
        {
          "type":"player",
          "info":{
            "name":"Kawhi Leonard",
            "position":"Small Forward",
            "picture":"https://i.ibb.co/b5sGk6L/40a233a203be2a30e6d50501a73d3a0a8ccc131fv2-128.jpg"
          }
        },
        {
          "type":"team",
          "info":{
            "name":"Los Angeles Clippers",
            "state":"California",
            "logo":"https://logos-download.com/wp-content/uploads/2016/04/LA_Clippers_logo_logotype_emblem.png"
          }
        }
      ]
    }
    """
    
    struct Response: Decodable {
        let data: [Datum]
    }
    
    struct League: Codable {
        let name: String
        let sport: String
        let website: URL
    }
    
    struct Player: Codable {
        let name: String
        let position: String
        let picture: URL
    }
    
    struct Team: Codable {
        let name: String
        let state: String
        let logo: URL
    }
    
    enum Datum: Decodable {
        case league(League)
        case player(Player)
        case team(Team)
        
        enum DatumType: String, Decodable {
            case league
            case player
            case team
        }
        
        private enum CodingKeys : String, CodingKey { case type, info }
     
        init(from decoder : Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            let type = try container.decode(DatumType.self, forKey: .type)
            switch type {
            case .league:
                let item = try container.decode(League.self, forKey: .info)
                self = .league(item)
            case .player:
                let item = try container.decode(Player.self, forKey: .info)
                self = .player(item)
            case .team:
                let item = try container.decode(Team.self, forKey: .info)
                self = .team(item)
            }
        }
    }
    
    do {
        let response = try JSONDecoder().decode(Response.self, from: Data(jsonString.utf8))
        let data = response.data
        print(data)
    //    receivedData.forEach { (datum) in
    //        let cell = Cell()
    //        cell.configure(with: datum.info.rowData)
    //        cells.append(cell)
    //    }
    //    cells.forEach({ print($0.title, $0.subtitle) })
    } catch {
        print(error)
    }
    

    In the cell switch on the type

    switch datum {
        case .league(let league): // so something with league
        case .player(let player): // so something with player
        case .team(let team): // so something with team
    }