2

I have this output JSON:

{
  "id": 42950262095,
  "name": "lol",
  "players": [
    {
      "avatar": {
        "userId": 25771876384,
        "userName": "yhht",
        "role": "Leader",
        "level": 40,
        "league": 0,
        "trophies": 1011,
        "donatedTroops": 0,
        "receivedTroops": 0
      }
    },
    {
      "avatar": {
        "userId": 146035414262,
        "userName": "ari",
        "role": "New Member",
        "level": 8,
        "league": 0,
        "trophies": 428,
        "donatedTroops": 0,
        "receivedTroops": 0
      }
    },
    {
      "avatar": {
        "userId": 300659467521,
        "userName": "cp 221",
        "role": "New Member",
        "level": 6,
        "league": 0,
        "trophies": 97,
        "donatedTroops": 0,
        "receivedTroops": 0
      }
    }
  ],
  "badge": 13000049,
  "status": "Anyone Can Join",
  "playerCount": 3,
  "score": 767,
  "requiredTrophies": 0,
  "warsWon": 0,
  "warsLost": 0,
  "warsTied": 0,
  "warFrequency": 0,
  "exp": 0,
  "level": 1,
  "description": "??lol????"
}

But the problem is the players array comes too early and part of the initial details are left out.

This is my code:

public void parseAvatar() throws IOException, JSONException{

        Game game = new Game();

        game.setId(is.readLong());
        game.setName(is.readString());
        game.setBadge(is.readInt());
        game.setStatus(status(is.readInt()));
        game.setPlayerCount(is.readInt());
        game.setScore(is.readInt());
        game.setRequiredTrophies(is.readInt());
        game.setWarsWon(is.readInt());
        game.setWarsLost(is.readInt());
        game.setWarsTied(is.readInt());
        is.readInt();
        game.setWarFrequency(is.readInt());
        is.readInt();
        game.setExp(is.readInt());
        game.setLevel(is.readInt());
        game.setDescription(is.readString());
        is.readInt();
        boolean a = is.readBoolean();

        if(a){
            is.readInt();
            is.readInt();
        }

        int memCount = is.readInt();
        /// Members!!

        int i = 0;
        while(i < memCount){
            PlayerAvatar avatar = new PlayerAvatar();
            avatar.setUserId(is.readLong());
            avatar.setUserName(is.readString());
            avatar.setRole(role(is.readInt()));
            avatar.setLevel(is.readInt());
            avatar.setLeague(is.readInt());
            avatar.setTrophies(is.readInt());
            avatar.setDonatedTroops(is.readInt());
            avatar.setReceivedTroops(is.readInt());
            is.readInt();
            is.readInt();
            is.readLong();
            is.readByte();
            is.readByte();
            is.readLong();

            GamePlayer player = new GamePlayer();
            player.setAvatar(avatar);
            game.addPlayers(player);
            i++;
        }


        json = new Gson().toJson(game);
        System.out.println();
    }


    private String role(int role) {
        String memberRole = "";

        if(role == 1){
            memberRole = "New Member";
        }   

        if(role == 2){
            memberRole = "Leader";
        }   

        if(role == 3){
            memberRole = "Elder";
        }   

        if(role == 4){
            memberRole = "Co Leader";
        }   

        return memberRole;
    }

    private String status(int statusint) {
        String type = null;
        if(statusint == 1){
            type = "Anyone Can Join";
        }
        if(statusint == 2){
            type = "Invite Only";
        }
        if(statusint == 3){
            type = "Closed";
        }
        return type;
    }

You can find details for the Game, PlayerAvatar and GamePlayer class in this post: https://stackoverflow.com/a/33048622

Does anyone have any idea on how I can get this ordered properly?

Community
  • 1
  • 1
  • 2
    "You can find details for the Game, PlayerAvatar and GamePlayer class in this post" - please put effort into making this question self-contained and minimal. You should be able to include a short but *complete* program demonstrating the problem, and in less code than you've presented here. – Jon Skeet Oct 10 '15 at 08:03
  • Possible duplicate of [How to keep fields sequence in Gson serialization](http://stackoverflow.com/questions/6365851/how-to-keep-fields-sequence-in-gson-serialization) – CupawnTae Oct 10 '15 at 08:17
  • CupawnTae linked you to GsonCustomObjectSerializer (http://stackoverflow.com/a/6366279/185565). Sometimes may want to use GsonStreamWriter(http://stackoverflow.com/questions/12155800/how-to-convert-hashmap-to-json-object-in-java/18444414#18444414). These two answers should give you a working solution. – Whome Oct 10 '15 at 08:26

2 Answers2

2

JSON objects are by definition unordered - you should not be relying on the order of properties. So what gson is doing is perfectly fine.

If for some reason you have no choice but to rely on the ordering, there are some solutions in this question: How to keep fields sequence in Gson serialization (e.g. use jackson instead of gson and use @JsonPropertyOrder, or implement a custom serializer).

With current gson and JDK implementations, reordering the members in your class will produce output in the corresponding order, however this is a very brittle solution. For example

  • The JDK doesn't guarantee the order in which fields are read through reflection, and it could change in future (it has changed in the past), which would break your code. (This is actually the least likely issue, but it's possible). In fact the JDK 8 documentation explicitly says

    The elements in the returned array are not sorted and are not in any particular order

  • Even if the JDK's ordering doesn't change, gson doesn't guarantee that it will return them in the same order. It currently does return them in the same order, but if for some reason the internals of gson changed e.g. to use an intermediate HashMap, the order would change and your code would break.
  • If someone else in the future is maintaining your class, it would be very surprising for them to find that just reordering the fields in the class would break the code - the ordering of fields in a java class should not change functionality.

If you're writing code only for your own amusement, or to use once to convert some data, it might be no big deal, but if you're writing code that others (or even Future You) will need to maintain, I strongly recommend you write code that doesn't rely on layers of assumptions, and/or on constant testing to make sure multiple undocumented behaviours haven't changed.

Spending a few minutes now incorporating code to actually guarantee the ordering of your fields could save hours or days of debugging down the line. This happens all the time in real life.

Community
  • 1
  • 1
CupawnTae
  • 14,192
  • 3
  • 29
  • 60
2

Gson creates json string according to your object's fields declaration order(Because it uses Java reflection ordering, the reflected fields are in deed in their declaration order on jdk 6, but it is changed after jdk 7)

Be sure that your JDK is 6. Gson is min 2.2.4 version.

private Long id;
private String name;
private List<Player> players;

prints as json string: first id, name then players.

{
  "id": 171799578198,
  "name": "Forum Striking",
  "players": [
    {
      "avatar": {
        "userId": 21393,
        "currentHomeId": 21393,
        "clanId": 171799578198
      }
    },
    {
      "avatar": {
        "userId": 64425223942,
        "currentHomeId": 64425223942,
        "clanId": 171799578198
      }
    }
  ]
}

private Long id;
private List<Player> players;
private String name;

prints as json string: id, players and name at the end.

{
  "id": 171799578198,
  "players": [
    {
      "avatar": {
        "userId": 21393,
        "currentHomeId": 21393,
        "clanId": 171799578198
      }
    },
    {
      "avatar": {
        "userId": 64425223942,
        "currentHomeId": 64425223942,
        "clanId": 171799578198
      }
    }
  ],
  "name": "Forum Striking"
}

So you should declare player list at the end of the your game object.

Sedat Polat
  • 1,631
  • 2
  • 18
  • 28
  • 4
    "Even if not guaranteed, it always works" - ouch. Until someone in gson decides it's better to represent things internally in a `HashMap` and in the next update the order changes, and your code breaks, but you've moved on to another job and don't know or care, and the next guy is left trying to figure out what on earth has just exploded. @ShivamPaw please don't ever rely on something that's "not guaranteed" in real life. – CupawnTae Oct 10 '15 at 10:40
  • It is not about gson, it is about java reflection(JDK 6 and upper version). Read this: http://stackoverflow.com/questions/5001172/java-reflection-getting-fields-and-methods-in-declaration-order – Sedat Polat Oct 10 '15 at 10:49
  • Of course it's about gson if he calls `gson.toJson()`. Just because the current gson implementation loops through properties using reflection and serializes in that order doesn't mean it always will - unless the spec guarantees it which it doesn't. And as you pointed out, it's not even guaranteed that the JDK won't change ordering - they already have after all. Yes, it's unlikely that either of these will change, and the code will *probably* never break, but why take the risk when there are ways of guaranteeing it's not going to happen? – CupawnTae Oct 10 '15 at 10:56
  • It's also relying on future maintainers knowing that you're relying on undocumented features and not e.g. re-sorting the fields alphabetically. So, you'd need to comment everything *very* carefully to make sure that won't happen. And somehow make sure people will read the comments, which isn't going to happen... – CupawnTae Oct 10 '15 at 11:00
  • @CupawnTae Also if you upgrade your system(JDK 9 in the future, and if they will not support reflection ordering), you have to do a reengineering on your project for many things. Do not worry about this. – Sedat Polat Oct 10 '15 at 11:00
  • Reflection ordering is the least likely thing to break, but you're missing the point. Do things right now and you're far less likely to have this kind of issue later. – CupawnTae Oct 10 '15 at 11:02
  • @CupawnTae if it was about gson, you would be right but it is about jdk. – Sedat Polat Oct 10 '15 at 11:20
  • Why do you keep saying that? The code says `json = new Gson().toJson(game);` - so if the implementation of `Gson.toJson(...)` changes, the order potentially changes. I don't understand why you're saying otherwise. – CupawnTae Oct 10 '15 at 11:30
  • Because you did not understand what you should focus. Gson have to use reflection. So point is reflection. Gson may change tojason() to toMyson(). This is not the point. Or change any thing they want. This is about while changing version of library you should test your code. – Sedat Polat Oct 10 '15 at 11:40
  • @SedatPolat, seems you missed the point of what CupawnTae had to say. Yes, Gson have to use reflection. But to present the data as Json to the client, it surely use some intermediary data structure to convert it. And if this intermediary data structure change, the order might not be guaranteed. And I completely agree with CupawnTae, you won't want your users complaining and stop using the app just because Gson changed, will you? Yes, you can always re-engineer your code, but the damage already been done. Suppose I'm new user and found the app not working, I won't bother to look at it again. – Mycotina Nov 18 '20 at 02:18