简体   繁体   中英

Dynamic (But known as another key value) JSON Decoding with Swift Decodable

I've looked at the answer of the similar question I could not understand the codingKey very well in addition it doesn't exactly apply to my case as the key is not completely "unknown" it is the value of a previous key. My Api:

{
  "api": {
    "results": 1,
    "fixtures": [
      {
        "homeTeam": {
          "team_name": "Tottenham"
        },
        "awayTeam": {
          "team_name": "Everton"
        },
        "lineups": {
          "Tottenham": {
            "formation": "4-2-3-1"
          },
          "Everton": {
            "formation": "4-2-3-1"
          }
        }
      }
    ]
  }
}

My Code:

class matchApiObject: Decodable
{
    let fixtures: [fixture]
    init (fixtures: [fixture])
    {
        self.fixtures = fixtures
    }
}

class fixture: Decodable
{
    let homeTeam: matchHomeTeamObject?
    let lineups: lineUpsObject?
    init (homeTeam: matchHomeTeamObject?, lineups: lineUpsObject?)
    {
        self.homeTeam = homeTeam
        self.lineups = lineups
    }
}

class matchHomeTeamObject: Decodable
{
    let team_name: String?
    init (team_name: String?)
    {
        self.team_name = team_name
    }
}

class lineUpsObject: Decodable
{
    struct homeLineUp: Decodable
    {
        let formation: String?
        init(formation: String?)
        {
            self.formation = formation
        }
    }
    struct awayLineUp: Decodable
    {
        let formation: String?
        init (formation: String?)
        {
            self.formation = formation
        }
    }
}

Obviously the keys of the lineups objects won't be "homeLineUp" but it will be as per the api example, the value of the homeTeam.team_name.

So what I imagine the solution to be is:

class lineUpsObject: Decodable
{
    struct homeTeam.team_name: Decodable
    {
        let formation: String?
        init(formation: String?)
        {
            self.formation = formation
        }
    }
    struct awayTeam.team_name: Decodable
    {
        let formation: String?
        init (formation: String?)
        {
            self.formation = formation
        }
    }
}

Which is not possible, I know I have to use a codingkey for this, but I can't understand how to declare the name of the key to be the value of a previous key value I did not understand what stringValue: String or intValue: Int do in the codingkey answer do or how they apply here, thank you.

A simple solution is to decode lineups as dictionary, I named all structs with starting capital letter to conform to the naming convention.

The init methods are not needed

struct Root : Decodable {
    let api : API
}

struct API : Decodable {
    let results : Int
    let fixtures : [Fixture]
}

struct Fixture : Decodable {
    let homeTeam, awayTeam: Team
    let lineups : [String:Lineup]
}

struct Team  : Decodable {
    let teamName : String
}

struct Lineup : Decodable {
    let formation : String
}

To convert the snake_cased keys to camelCased struct members add the appropriate strategy

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

A more sophisticated solution is to put the formation data into the Team struct but this requires to implement the init(from decoder:) method in Fixture .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM