2

I have a User class defined as:

User.java

package model;

import java.util.List;
import java.util.Map;

public class User {
    private final Map<String, List<String>> accountTransactionsMap;

    public User(final Map<String, List<String>> accountTransactionsMap) {
        this.accountTransactionsMap = accountTransactionsMap;
    }

    public Map<String, List<String>> getAccountTransactionsMap() {
        return accountTransactionsMap;
    }
}

I am calling a REST API that returns the following response:

{  
   "username1":{  
      "456":[  

      ],
      "123":[  

      ],
      "789":[  

      ]
   },
   "username2":{  
      "123":[  

      ],
      "456":[  

      ],
      "789":[  

      ]
   },
   "username3":{  
      "789":[  

      ],
      "123":[  

      ],
      "456":[  
         "transaction10",
         "transaction6",
         "transaction9",
         "transaction3"
      ]
   }
}

I would like to be able to parse through the response and store it in a User object.

I have tried the following:

Test.java

public class Test {
    public static void main(final String[] args) {
        final String response = "{\"username1\":{\"456\":[],\"123\":[],\"789\":[]},\"username2\":{\"123\":[],\"456\":[],\"789\":[]},\"username3\":{\"789\":[],\"123\":[],\"456\":[\"transaction10\",\"transaction6\",\"transaction9\",\"transaction3\"]}}";
        final Gson gson = new Gson();
        final Type map = new TypeToken<Map<String, User>>(){}.getType();
        final Map<String, User> result = gson.fromJson(response, map);
        System.out.println(result);

        if (result != null) {
            for (final Map.Entry<String, User> entry : result.entrySet()) {
                System.out.println("username: " + entry.getKey());
                final User user = entry.getValue();
                System.out.println("transactions: " + user.getAccountTransactionsMap());
            }
        }
    }
}

This yields output:

{username1=model.User@80ec1f8, username2=model.User@1445d7f, username3=model.User@6a396c1e}
username: username1
transactions: null
username: username2
transactions: null
username: username3
transactions: null

I expect output:

{username1=model.User@80ec1f8, username2=model.User@1445d7f, username3=model.User@6a396c1e}
username: username1
transactions: {123=[],456=[],789=[]}
username: username2
transactions: {123=[],456=[],789=[]}
username: username3
transactions: {123=[],456=["transaction10", "transaction6", "transaction9", "transaction3"],789=[]}

How can I parse the accountId and the list of transactionIds into its own map as a variable in my User class?

Edit: I suppose the question really becomes, how can I create a custom TypeToken for my User class?

raphattack
  • 179
  • 4
  • 12

1 Answers1

0

Instead of User class you need to use Map<String, Map<String, List<String>>>:

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        final Gson gson = new Gson();
        final Type map = new TypeToken<Map<String, Map<String, List<String>>>>(){}.getType();
        final Map<String, Map<String, List<String>>> result = gson.fromJson(new FileReader(jsonFile), map);
        System.out.println(result);

        if (result != null) {
            for (final Map.Entry<String, Map<String, List<String>>> entry : result.entrySet()) {
                System.out.println("username: " + entry.getKey());
                final Map<String, List<String>> user = entry.getValue();
                System.out.println("transactions: " + user);
            }
        }
    }
}

Above code prints:

{username1={456=[], 123=[], 789=[]}, username2={123=[], 456=[], 789=[]}, username3={789=[], 123=[], 456=[transaction10, transaction6, transaction9, transaction3]}}
username: username1
transactions: {456=[], 123=[], 789=[]}
username: username2
transactions: {123=[], 456=[], 789=[]}
username: username3
transactions: {789=[], 123=[], 456=[transaction10, transaction6, transaction9, transaction3]}

If you really need, you can create User object after parsing.

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • Thanks, this code is working, however if the JSON response changes then it will be difficult to maintain. I'm going to edit my post to ask if there's a way to define a custom `TypeToken` for a class so that I can instead pass `TypeToken` or something of the sort. – raphattack Feb 22 '19 at 00:47
  • @raphattack, you can try to write custom deserialiser like it is pointed in this [question](https://stackoverflow.com/questions/10267749/how-to-parse-a-json-with-dynamic-key-in-android-by-using-gson). If you can use `Jackson` maybe try it. It has a little bit better deserialisation options. See this [question](https://stackoverflow.com/questions/6796662/using-gson-to-parse-a-json-with-dynamic-key-and-value-in-android). You have schema lik: `dynamic-key`->`dynamic-key`->`array` and it could look strange but it is pretty normal structure which we can handle using `Map`s and `List`s. – Michał Ziober Feb 22 '19 at 01:07
  • 1
    `Jackson` was exactly what I was looking for. Thanks for the suggestion! – raphattack Feb 23 '19 at 22:16