1

I've been making a simulated sports pyramid (sort of like the English football pyramid) in Java with various leagues and a complex system of tiers and leagues and tournaments and promotion/relegation between them. It's pretty complicated and it's a lot of information, so I didn't want to hardcode it, so I've been trying to put all the information defining the structure of the whole system in configuration files instead. Initially, I put that information in a series of csv files that look like this:

3,4,-3,5
3,2,2,1
4,3,2,3,0
6,5,1,2
7,6,5,2,3,0
8,7,1,2,9

I wrote a function that parses it, and that works absolutely fine, but, just looking at it, that file's pretty incomprehensible; I had to write a whole guide so that I could interpret what these files meant (and that's not even considering how awful it would be for anyone else if they took a look at this).

So, I want to put this data in some sort of key:value system instead for it to be more readable. The data itself is defining a whole bunch of different tournaments, leagues, etc., so it'd be ideal if the data could be nested, so I don't think Properties will work for it (although I'd prefer something that wouldn't need external libraries).

I've worked with JSON before with other languages, so I impulsively decided to encode all the data into a config.json file before I knew how I would deserialize it into my application.

My data (in JSON at least), looks like something like this:

{
    "tiers" : [
        {
            "id" : 100,
            "name" : "League of Kings",
            "scheduleString" : "standard",
            "leagues" : [16]
        },
        {
            "id" : 200,
            "name" : "The Neck",
            "scheduleString" : "standard",
            "leagues" : [8]
        },
        {
            "id" : 300,
            "name" : "Group Tier",
            "scheduleString" : "standard",
            "leagues" : [5,5,5,5,5,5]
        } 
    ],
    "movements" : [
        {
            "origin" : [100],
            "destination" : [200],
            "comparator" : "standard",
            "ranks" : [15,16],
            "priority" : 1,
            "group" : 0
        },
        {
            "origin" : [100],
            "destination" : [1],
            "comparator" : "random",
            "ranks" : [1],
            "priority" : 2,
            "group" : 0
        },
        {
            "origin" : [200],
            "destination" : [1],
            "comparator" : "standard",
            "ranks" : [8],
            "priority" : 1,
            "group" : 0
        },
        {
            "origin" : [200],
            "destination" : [100],
            "comparator" : "standard",
            "ranks" : [1,2,3],
            "priority" : 1,
            "group" : 0
        }
    ],
    "tournaments" : [
        {
            "origin" : [[300]],
            "destination" : [200],
            "comparator" : ["standard"],
            "ranks" : [[1]],
            "output" : 2,
            "winners" : true,
            "special" : false,
            "priority" : 1,
            "group" : 0
        }
    ],
    "topTier" : 100,
    "maxRounds" : 50,
    "multichampThreshold" : 5,
    "deadCollectionId" : 1
}

The main problem that I have is that there's so many JSON Java libraries out there, I have no idea which one (if any) will work best for my application here.

I have my own constructors for these objects that I'd like to use, so I don't really want to use the direct JSON -> object mapping tools that I've in some of the libraries out there (edit: this is called data binding I think?).

I'd just like to be able to do something sort of like this:

JSONObject obj = JSON.parseFile("config.json");
ArrayList<Tier> tiers = new ArrayList<Tier>();
for (JSONObject tierObj : obj.getArray("tiers")) {
    Tier newTier = new Tier(tierObj.getInt("id"), tierObj.getString("name"));
    tiers.add(newTier);
}

Which Java JSON library do you think would be the best for me to use here? Or should I switch back to using some other sort of data encoding (Properties, XML, etc.)? Or should I just write my own JSON parser to do the work instead?

Here are the relevant class definitions:

public final class Tier {
    private final int id;
    private final String name;
    private final League[] leagues;
}

public class TeamCollection {
    private final int id;
    private final String name;
    private final int maxLength;
    private ArrayList<Team> teams;
}

public final class League extends TeamCollection implements Comparable<League> {
    private final String scheduleString;
    private int[][][] schedule;
}

public class MovementHandler {
    private final TeamCollection[] origin;
    private final TeamCollection[] destination;
    private final RankHandler rh;
    private final int priority;
    private final int group;
    private boolean active;
}

public class TournamentHandler {
    private final TeamCollection[][] origins;
    private final TeamCollection[] destination;
    private final RankHandler[] rhs;
    private final Tournament tournament;
    private final int priority;
    private final int group;
    private boolean active;
}


public final class Tournament {
    private final int id;
    private final String name;
    private final int numOutputTeams;
    private final boolean winnersBracket;
    private final boolean special;
    private boolean printPlayerMatches;
    private ArrayList<Team> inputTeams;
    private ArrayList<Team> outputTeams;
    private ArrayList<Team> currentTeams;
    private int round;
}

public class RankHandler {
    private final Comparator<Team> comparator;
    private final int[] ranks;
    private final boolean random;
    private final boolean all;
}

So, just to clarify what exactly I want the configuration file to define, I want the "tiers" section of the configuration file to define a list of Tiers (and the Leagues that are contained within), and I want the "movements" section to define a list of Movement Handlers (and the Rank Handlers that they use), and I want the "tournaments" section to define a list of Tournament Handlers (and the Rank Handlers that they use and the Tournaments that they are associated with; a 1:1 ratio there). And I also have a couple of normal configuration things that I need to set, but I don't think that should be a problem.

  • You can try making a JSON string out of your data like: `obj.getArray("tier").toString()` and mapping it to Java POJO using [GSON](https://zetcode.com/java/gson/), for example. – Krzysztof Majewski Feb 15 '22 at 20:24
  • @KrzysztofMajewski My classes are a bit complicated and I've already built complex constructors that I'm happy with, so I'd rather not use data binding to a POJO if I can avoid it. All I really want to be able to do is to get a string or an array of ints or whatever by using a key, and also to iterate through a list of items in an array in the json, getting properties from each of those items by using keys. Can GSON do that, or would I have to use the binding stuff to use GSON? – Riggs Markham Feb 15 '22 at 20:50
  • 1
    GSON maps JSON to Java Object. Or transforms Java Objects into JSON. If you want to manually construct objects from JSON then your current solution, using `JSONObject` seems appropriate. – Krzysztof Majewski Feb 15 '22 at 20:53
  • 1
    Use [YAML](https://en.wikipedia.org/wiki/YAML). If you’re using spring, you can load it “for free” on start up as config. If you insist on json, use [`ObjectMapper.readValue()`](https://www.baeldung.com/jackson-object-mapper-tutorial#2-json-to-java-object) – Bohemian Feb 15 '22 at 20:54
  • @Bohemian Because I'm dumb, I'm not using anything like Spring at the moment. Do you know of any libraries that can parse yaml in the way that I want to parse json here? – Riggs Markham Feb 15 '22 at 21:04
  • Try [this](https://stackoverflow.com/a/56275697/256196) – Bohemian Feb 15 '22 at 21:25
  • 1
    @riggs “dumb” and “beginner” are different things – Bohemian Feb 15 '22 at 21:38
  • Can you please add the class defs for Tier, Movement and Tournament to the question? (you can leave out getters and setters for brevity - we can fill in the blanks) – Bohemian Feb 15 '22 at 22:53
  • @Bohemian do you want all the methods for those too ... because even without the getters and setters, that wouldn't be brief (like 500 lines total) – Riggs Markham Feb 16 '22 at 01:24
  • 1
    Just the fields will do. – Bohemian Feb 16 '22 at 02:54
  • Added the fields of what I thought were the relevant classes (I think that's all the objects that I want created via the config file). – Riggs Markham Feb 16 '22 at 06:01
  • 1
    I can't help you. The field `League[] leagues` in `Tier` is an array of `League` objects, but the config is a `List`, which is not going to work (`List leagueIds`?). Remove `final` entirely - it adds little/no value and `final` fields don't play nice with deserializers. Replace all `int[]` with `List` and `ArrayList` with `List`: see [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). Try [Lombok](https://projectlombok.org), which eradicates [boilerplate plate](https://en.wikipedia.org/wiki/Boilerplate_code) code. – Bohemian Feb 16 '22 at 06:32
  • Thank you so much for your help anyway, I knew it was a bit of a long shot with my *highly* nonstandard architecture, but I'll look into using yaml, and I'm very interested in Lombok. I might not use it in this project, but it sounds very promising for future ones. – Riggs Markham Feb 16 '22 at 07:55

0 Answers0