2

I'm using Retrofit 2 in combination with Gson and RxJava. My JSON data looks something like this:

{
    "groups": {
         "1": {
              "name": "First group",
              "type": "some data",
              // ... more data
         },
         "2": {
              "name": "Second group",
              "type": "some data",
              // ... more data
         },
         "3": {
              "name": "Third group",
              "type": "some data",
              // ... more data
         }
         // 4, 5, 6, etc...
    },
    // ... more data
}

In the example above the "keys" 1, 2, 3 are integers but they can also be unique strings. I want to map this JSON data to something like this:

public class MyData {
    @SerializedName("groups")
    private Map<String, Group> mGroups;

    // ... more data
}

public class Group {
    // TODO: This should be "1", "2", "3", etc.
    @SerializedName(???)
    private String mId;

    // This should be "First group", "Second group", "Third group", etc.
    @SerializedName("name")
    private String mName;

    // This should be "some data"
    @SerializedName("type")
    private String mType;

    // ... more data
}

What's the best way to put the dynamic key (1, 2, 3) in the Group object as well? I'm not hosting the JSON data myself so it cannot be changed to another format.

Thomas Vos
  • 12,271
  • 5
  • 33
  • 71
  • Why is your groups JSON not an array of objects? because each group has a distinct int key value you can't parse it in an easy fashion. any why I found something that is very related to your question, see this q&a: https://stackoverflow.com/questions/14713736/gson-parse-json-with-array-with-different-object-types – Avi Levin Jul 16 '17 at 17:47
  • @AviLevin Thanks for your reply. I don't have control over the JSON data so I cannot change it. The values 1, 2, 3 were just an example, they don't have to be integers but can also be unique strings. I don't see how the other question is related as it doesn't have dynamic "keys". – Thomas Vos Jul 16 '17 at 17:57
  • One way to do it is to just put the map keys into the group objects `for( String key : mGroups.keySet() ) mGroups.get(key).setmId(key); ` The other way is to use a deserializer which I believe is more complex for what you really want – Hugo sama Jul 16 '17 at 18:51
  • 1
    @ThomasVos, check again the accepted answer in the q/a that I sent. you can see that they built a unique Deserializer class which they later used to parse the JSON data. with unique keys, this is your best option. let me know if you need an example for your case. – Avi Levin Jul 17 '17 at 08:24
  • @AviLevin I would appreciate an example. – Thomas Vos Jul 17 '17 at 10:40
  • @Thomas - See my answer below. it's just an example (wrote it quickly). I know your JSON is bigger. therefore you'll only need to expand the Deserializer class that I wrote. let me know if you got any issues. :) – Avi Levin Jul 17 '17 at 19:00

1 Answers1

3

Step A - Create a group class:

public class Group {
    String name;
    String type;
}

Step B - Create a groups class:

public class Groups {
    List<Group> userList;
}

Step C - Create a GSON Deserializer class

public class MyDeserializer implements JsonDeserializer<Groups> {
    private final String groups_key = "groups";

    @Override
    public Groups deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Groups groups = new Groups();
        JsonObject object = json.getAsJsonObject().getAsJsonObject(groups_key);
        Map<String, Group> retMap = new Gson().fromJson(object, new TypeToken<HashMap<String, Group>>() {}.getType());

        List<Group> list = new ArrayList<Group>(retMap.values());
        groups.userList = list;

        return groups;
    }
}

Step D - Register your Deserializer when you create your Gson object

Gson gson = new GsonBuilder()
                        .registerTypeAdapter(Groups.class, new MyDeserializer()).create();

Step E - Convert your JSON object via. Gson

Groups groups = gson.fromJson(jsonExmple, Groups.class);

Notes:

  • When your JSON object gets bigger you can expand your Group/Groups classes and add more variables. Keep in mind that you will need to reflect everything in your Deserializer class. :)
  • Use @SerializedName annotation when your variable got a different name from your JSON key
Avi Levin
  • 1,868
  • 23
  • 32
  • 1
    Thank you very much for giving an example. This really helped me understand how this works. I think I'll do something like this: https://gist.github.com/anonymous/101d7272b3f452cad5c54417b6dc92db. I don't really like the for loop but I guess this is the only way to get the ids in the Group class. If you know a better way please let me know. – Thomas Vos Jul 17 '17 at 19:37
  • np. yep, this is the way to go if you need to set the key in addition to the group. Good luck! – Avi Levin Jul 17 '17 at 20:05
  • Hi Avi, the documentation says it's recommended to use `TypeAdapter` instead. My JSON data is really complex, so this method might affect performance. I tried another method using a `TypeAdapter`. Here's a sample what I did: https://gist.github.com/Thomas-Vos/8b2bd968a7008c0cd31d9a65caf1c00f. (credits go to https://stackoverflow.com/a/11272452/4583267). So instead of usingn a custom deserializer it just modifies the JSON data before it's converted. What do you think about this? Thanks, Thomas. – Thomas Vos Jul 18 '17 at 11:02