JSON to JAVA POJO with dynamic key

846 views Asked by At

I am trying to convert from JSON to JAVA POJO.

My JSON-string looks like the following:

{
  "api": {
    "results": 1,
    "fixtures": [
      {
        "fixture_id": 38480,
        "league_id": 95,
        "lineups": {
          "Lecce": {
            "coach": "F. Liverani",
            "coach_id": 2442,
            "formation": "4-2-3-1",
            "startXI": [
              {
                "team_id": 867,
                "player_id": 31719,
                "player": "M. Vigorito",
                "number": 22,
                "pos": "G"
              },
              {
                "team_id": 867,
                "player_id": 31721,
                "player": "M. Calderoni",
                "number": 27,
                "pos": "D"
              },
              {
                "team_id": 867,
                "player_id": 31725,
                "player": "F. Lucioni",
                "number": 25,
                "pos": "D"
              }
            ],
            "substitutes": [
              {
                "team_id": 867,
                "player_id": 31744,
                "player": "S. Palombi",
                "number": 14,
                "pos": "F"
              },
              {
                "team_id": 867,
                "player_id": 31740,
                "player": "A. Tabanelli",
                "number": 23,
                "pos": "D"
              },
              {
                "team_id": 867,
                "player_id": 31739,
                "player": "M. Scavone",
                "number": 30,
                "pos": "M"
              }
            ]
          },
          "Spezia": {
            "coach": "P. Marino",
            "coach_id": 2899,
            "formation": "4-2-3-1",
            "startXI": [
              {
                "team_id": 515,
                "player_id": 30820,
                "player": "E. Lamanna",
                "number": 1,
                "pos": "G"
              },
              {
                "team_id": 515,
                "player_id": 30829,
                "player": "C. Terzi",
                "number": 19,
                "pos": "D"
              },
              {
                "team_id": 515,
                "player_id": 30824,
                "player": "E. Capradossi",
                "number": 13,
                "pos": "D"
              },
              {
                "team_id": 515,
                "player_id": 30837,
                "player": "L. Mora",
                "number": 6,
                "pos": "M"
              }
            ],
            "substitutes": [
              {
                "team_id": 515,
                "player_id": 30848,
                "player": "D. Okereke",
                "number": 21,
                "pos": "G"
              },
              {
                "team_id": 515,
                "player_id": 30832,
                "player": "M. Crimi",
                "number": 15,
                "pos": "M"
              },
              {
                "team_id": 515,
                "player_id": 30842,
                "player": "S. Bidaoui",
                "number": 26,
                "pos": "D"
              }
            ]
          }
        }
      }
    ]
  }
}

My problem is that the "Team" names are dynamical and will change for each match fixture JSON String I receive.

I have used http://www.jsonschema2pojo.org/ to get ready files but then it looks like the following:

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.codehaus.jackson.map.annotate.JsonSerialize;

@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
@JsonPropertyOrder({
    "Lecce",
    "Spezia"
})
public class Lineups implements Serializable
{

    @JsonProperty("Lecce")
    private Lecce lecce;
    @JsonProperty("Spezia")
    private Spezia spezia;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();
    private final static long serialVersionUID = -2766671198131939159L;

    /**
     * No args constructor for use in serialization
     * 
     */
    public Lineups() {
    }

    /**
     * 
     * @param lecce
     * @param spezia
     */
    public Lineups(Lecce lecce, Spezia spezia) {
        super();
        this.lecce = lecce;
        this.spezia = spezia;
    }

    @JsonProperty("Lecce")
    public Lecce getLecce() {
        return lecce;
    }

    @JsonProperty("Lecce")
    public void setLecce(Lecce lecce) {
        this.lecce = lecce;
    }

    @JsonProperty("Spezia")
    public Spezia getSpezia() {
        return spezia;
    }

    @JsonProperty("Spezia")
    public void setSpezia(Spezia spezia) {
        this.spezia = spezia;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

}

This works fine as long as it is Lecce and Spezia. If it is other teams I will not get the names and the other information.

I have succeded to fix this in another way when I made the mapping myself. Then I solved it like this:

public static class Lineups {
        private Map<String, Team> team = new LinkedHashMap<>();
        
        public Map<String, Team> getTeam() {
        return team;
        }

        public void setTeam(Map<String, Team> team) {
        this.team = team;
        }

        @JsonAnySetter
        public void setTeam(String key, Team value) {
        this.team.put(key, value);
        }
             
        public Lineups() {
            
        }
    }

But I wanted to organize it and use anotations since I got other problems with the JSON files I recieved in my earlier setup. I have tried to use something similar with Map<String, Team> team = new LinkedHashMap<>() in my new setup but I canĀ“t get it to work.

Are there anybody that can assist me in how to solve this and get it to work with the files I get from http://www.jsonschema2pojo.org/.

2

There are 2 answers

2
Andreas On BEST ANSWER

Talking it from the top:

First part is an object with field named api:

{
    "api": {
class Root {
    @JsonProperty("api") private API api;
}

Next part is an object with two fields named results and fixtures, where fixtures is an array or a List, and we generally prefer using List:

        "results": 1,
        "fixtures": [
class API {
    @JsonProperty("results")  private int results;
    @JsonProperty("fixtures") private List<Fixture> fixtures; // array
}

Next part is an object with three fields named fixture_id, league_id, and lineups, where lineups is an associative array, which in Java is a Map<String, ?>:

            {
                "fixture_id": 38480,
                "league_id": 95,
                "lineups": {
                    "Lecce": {
class Fixture {
    @JsonProperty("fixture_id") private int fixtureId;
    @JsonProperty("league_id")  private int leagueId;
    @JsonProperty("lineups")    private Map<String, Lineup> lineups; // associative array
}

The rest is pretty straight-forward:

                        "coach": "F. Liverani",
                        "coach_id": 2442,
                        "formation": "4-2-3-1",
                        "startXI": [
                            ...
                        ],
                        "substitutes": [
                            ...
                        ]
class Lineup {
    @JsonProperty("coach")       private String coach;
    @JsonProperty("coach_id")    private int coachId;
    @JsonProperty("formation")   private String formation;
    @JsonProperty("startXI")     private List<Player> startXI;
    @JsonProperty("substitutes") private List<Player> substitutes;
}
                            {
                                "team_id": 867,
                                "player_id": 31719,
                                "player": "M. Vigorito",
                                "number": 22,
                                "pos": "G"
                            },
class Player {
    @JsonProperty("team_id")   private int teamId;
    @JsonProperty("player_id") private int playerId;
    @JsonProperty("player")    private String player;
    @JsonProperty("number")    private int number;
    @JsonProperty("pos")       private String pos;
}
0
Y2020-09 On

I usually get really into code comments (Code Documentation), but with things like JSON Parsing - the lines of code are so self-explanatory, that it just seems unnecessary. I am practicing using the JSON Library javax.json...

I do not use Java's Component Annotations (used to be called Java Beans), so writing constructors and getters the regular way is what I'm used to.

Here is my version... It is longer than the other version, but highly readable and adding / removing methods is extremely simple.

import java.io.*;
import javax.json.*;
import java.util.*;

public class S
{
    public static class Player
    {
        public final String name, position;
        public final int playerId, teamId, number;
        public final boolean starting;

        public Player(String name, String position, int playerId, int teamId, int number, boolean starting)
        { 
            this.name=name; this.position=position;
            this.playerId=playerId; this.teamId=teamId; this.number=number;
            this.starting=starting;
        }

        public String toString()
        {
            return
                "Name:      " + name      + '\n' +
                "Position:  " + position  + '\n' +
                "Number:    " + number    + '\n' +
                "Starting:  " + (starting ? "Starting Lineup" : "Substitute") + '\n';
        }
    }

    public static class Team extends Vector<Player>
    {
        public final String teamName, coachName, formation;
        public final int coachId;

        public Team(String teamName, String coachName, int coachId, String formation)
        {
            this.teamName=teamName; this.coachName=coachName; this.coachId=coachId;
            this.formation=formation;
        }

        public Player findByName(String name)
        {
            for (Player player : this) if (player.name.equalsIgnoreCase(name)) return player;
            return null;
        }

        public Player findByNumber(int number)
        {
            for (Player player : this) if (player.number == number) return player;
            return null;
        }

        public Player findByPosition(String position)
        {
            for (Player player : this) if (player.position.equalsIgnoreCase(position)) return player;
            return null;
        }

        public String toString()
        {
            StringBuffer sb = new StringBuffer();
            sb.append(
                "Team Name:    " + teamName + '\n' +
                "Coach Name:   " + coachName + '\n' +
                "Formation:    " + formation + "\n\n"
            );
            for (Player player : this) sb.append(player.toString() + "\n");
            return sb.toString();
        }
    }

    public static void main(String[] argv) throws IOException
    {
        Reader r = new FileReader("teams.json");
        JsonObject teamsList = Json
            .createReader(r)
            .readObject()
            .getJsonObject("api")
            .getJsonArray("fixtures")
            .getJsonObject(0)
            .getJsonObject("lineups");
        
        Vector<Team> teams = new Vector<>();

        for (String teamName : teamsList.keySet())
        {
            JsonObject      teamObj         = teamsList.getJsonObject(teamName);
            String          coachName       = teamObj.getString("coach");
            int             coachId         = teamObj.getInt("coach_id");
            String          formation       = teamObj.getString("formation");
            JsonArray       startingLineup  = teamObj.getJsonArray("startXI");
            JsonArray       substitutes     = teamObj.getJsonArray("substitutes");
            List<JsonArray> lineups         = new Vector<>();
            Team            team            = new Team(teamName, coachName, coachId, formation);
            int             count           = 0;

            teams.add(team);
            lineups.add(startingLineup);
            lineups.add(substitutes);

            for (JsonArray lineup : lineups)
            {
                boolean starting = (count++ == 0);
                for (JsonObject playerObj : lineup.getValuesAs(JsonObject.class))
                {
                    int     teamId          = playerObj.getInt("team_id");
                    int     playerId        = playerObj.getInt("player_id");
                    String  name            = playerObj.getString("player");
                    int     number          = playerObj.getInt("number");
                    String  position        = playerObj.getString("pos");

                    Player player = new Player(name, position, playerId, teamId, number, starting);
                    team.add(player);
                }
            }
        }

        for (Team team : teams) System.out.println(team.toString() + "\n");
    }
}

This code will produce this output:

Team Name:    Lecce
Coach Name:   F. Liverani
Formation:    4-2-3-1

Name:      M. Vigorito
Position:  G
Number:    22
Starting:  Starting Lineup

Name:      M. Calderoni
Position:  D
Number:    27
Starting:  Starting Lineup

Name:      F. Lucioni
Position:  D
Number:    25
Starting:  Starting Lineup

Name:      S. Palombi
Position:  F
Number:    14
Starting:  Substitute

Name:      A. Tabanelli
Position:  D
Number:    23
Starting:  Substitute

Name:      M. Scavone
Position:  M
Number:    30
Starting:  Substitute


Team Name:    Spezia
Coach Name:   P. Marino
Formation:    4-2-3-1

Name:      E. Lamanna
Position:  G
Number:    1
Starting:  Starting Lineup

Name:      C. Terzi
Position:  D
Number:    19
Starting:  Starting Lineup

Name:      E. Capradossi
Position:  D
Number:    13
Starting:  Starting Lineup

Name:      L. Mora
Position:  M
Number:    6
Starting:  Starting Lineup

Name:      D. Okereke
Position:  G
Number:    21
Starting:  Substitute

Name:      M. Crimi
Position:  M
Number:    15
Starting:  Substitute

Name:      S. Bidaoui
Position:  D
Number:    26
Starting:  Substitute