1

I'm trying to create a struct to model the following bit of JSON, where the high level properties ("Blue Team" and "Green Team") don't have specified keys.

{
  "Teams": [
    {
      "Blue Team": {
        "motto": "We're the best",
        "players": [
          {
            "name": "Bob",
            "skill": "jumping really high",
            "birthday": 1546326611,
          },
          {
            "name": "Julie",
            "skill": "really strong",
            "birthday": 1546413133,
          },
          {
            "name": "Kirsten",
            "skill": "smarty pants",
            "birthday": 1546499716,
          }
        ]
      },
      "Green Team": {... // same structure as above }
    }
  ]
}

I believe I'm close, but I'm not sure how to represent the Blue Team and Red Team. This is what I have so far:

struct AllTeams: Codable {
    let Teams: [String : Team]

    struct Team: Codable {
        //let <property>: ???
    }

    struct ???: Codable {
        let motto: String
        let players: [Player]
    }

    struct Player: Codable {
        let name: String
        let skill: String
        let birthday: Int // will need to convert this
    }
}
Daniel
  • 55
  • 2
  • 6
  • Why isn't `motto` and `players` both properties of `Team` as it is in your source document? – tadman Jan 20 '19 at 20:29

3 Answers3

1

I suppose you want a [String: Team] in your AllTeams struct?

You can do this:

struct AllTeams: Decodable {
    let teams: [String: Team]

    enum CodingKeys: String, CodingKey {
        case teams = "Teams"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        teams = (try container.decode([[String: Team]].self, forKey: .teams))[0]
    }
}

struct Team: Codable {
    let motto: String
    let players: [Player]
}

struct Player: Codable {
    let name, skill: String
    let birthday: Date
}

Decoding:

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let teams = try! decoder.decode(AllTeams.self, from: json)
print(teams.teams["Blue Team"].motto)
Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

Correct json ( as your json is in-complete and contains wrong , at end of birthday keys )

{
    "Teams": [{
        "Blue Team": {
            "motto": "We're the best",
            "players": [{
                    "name": "Bob",
                    "skill": "jumping really high",
                    "birthday": 1546326611
                },
                {
                    "name": "Julie",
                    "skill": "really strong",
                    "birthday": 1546413133
                },
                {
                    "name": "Kirsten",
                    "skill": "smarty pants",
                    "birthday": 1546499716
                }
            ]
        },
        "Green Team": {
            "motto": "We're the best",
            "players": [{
                    "name": "Bob",
                    "skill": "jumping really high",
                    "birthday": 1546326611
                },
                {
                    "name": "Julie",
                    "skill": "really strong",
                    "birthday": 1546413133
                },
                {
                    "name": "Kirsten",
                    "skill": "smarty pants",
                    "birthday": 1546499716
                }
            ]
        }
    }]
}

for 2 keys

struct Root: Codable {
    let teams: [Team]

    enum CodingKeys: String, CodingKey {
        case teams = "Teams"
    }
}

struct Team: Codable {
    let blueTeam, greenTeam: BlueTeamClass

    enum CodingKeys: String, CodingKey {
        case blueTeam = "Blue Team"
        case greenTeam = "Green Team"
    }
}

struct BlueTeamClass: Codable {
    let motto: String
    let players: [Player]
}

struct Player: Codable {
    let name, skill: String
    let birthday: Int
}

for dynamic keys

struct Root: Codable {
    let teams: [[String:Item]]

    enum CodingKeys: String, CodingKey {
        case teams = "Teams"
    }
}

struct Item: Codable {
    let motto: String
    let players: [Player]
}

struct Player: Codable {
    let name, skill: String
    let birthday: Int
}

Decode

do {
     let res = try JSONDecoder().decode(Root.self,from:data)
     print(res)
   }
 catch {
    print(error)
}        
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
0

You are indeed pretty close.

As you want to decode the teams as dictionary change the structs to

struct AllTeams: Codable {
    let Teams: [[String : Team]]

    struct Team: Codable {
        let motto: String
        let players: [Player]
    }

    struct Player: Codable {
        let name: String
        let skill: String
        let birthday: Date
    }
}

and decode

 let decoder = JSONDecoder()
 decoder.dateDecodingStrategy = .secondsSince1970
 let result = try decoder.decode(AllTeams.self, from: data)

The birthday integer is decoded as Date

For a more sophisticated solution to include the team keys in the Team struct with custom CodingKeys take a look at this answer

Note :

You are encouraged to conform to the naming convention and add CodingKeys to map the uppercased keys to lowercased struct members

vadian
  • 274,689
  • 30
  • 353
  • 361