How can I use Google GSON to deserialize a JSON array into a a Collection of a generic type?

generics, gson, java

Solution

Sadly, you can't. The generic type has been erased by the compiler, and there's no way for Gson to know what types you have or what their fields are.

Problem

I'm writing some code that is going to be used to retrieve resources from a website. It all sort of looks like this: ``` public Collection<Project> getProjects() { String json = getJsonData(methods.get(Project.class)); //Gets a json list, ie [1, 2, 3, 4] Gson gson = new Gson(); Type collectionType = new TypeToken<Collection<Project>>() {}.getType(); return gson.fromJson(json, collectionType); } ``` So naturally I tried abstracting it using Java generics. ``` /* * Deserialize a json list and return a collection of the given type. * * Example usage: getData(AccountQuota.class) -> Collection<AccountQuota> */ @SuppressWarnings("unchecked") public <T> Collection<T> getData(Class<T> cls) { String json = getJsonData(methods.get(cls)); //Gets a json list, ie [1, 2, 3, 4] Gson gson = new Gson(); Type collectionType = new TypeToken<Collection<T>>(){}.getType(); return (Collection<T>) gson.fromJson(json, collectionType); } ``` The generic version of the code doesn't quite work though. ``` public void testGetItemFromGetData() throws UserLoginError, ServerLoginError { Map<String,String> userData = GobblerAuthenticator.authenticate("foo@example.com", "mypassword"); String client_key = userData.get("client_key"); GobblerClient gobblerClient = new GobblerClient(client_key); ArrayList<Project> machines = new ArrayList<Project>(); machines.addAll(gobblerClient.getData(Project.class)); assertTrue(machines.get(0).getClass() == Project.class); Log.i("Machine", gobblerClient.getData(Project.class).toString()); } java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.gobbler.synchronization.Machine at com.gobblertest.GobblerClientTest.testGetItemFromGetData(GobblerClientTest.java:53) at java.lang.reflect.Method.invokeNative(Native Method) at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169) at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154) at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551) ``` The class in question: ``` import java.util.Map; public class Project { private int total_bytes_stored; private String name; private String user_data_guid; private int seqnum; private String guid; private Map current_checkpoint; private Map<String, String> upload_folder; // TODO: schema_version private boolean deleted; // TODO: download_folders public Project() {} // No args constructor used for GSON } ``` I'm not quite familiar with all the details of Java generics or GSON internals and my search has not been particularly informative. There a bunch of questions here on SO, but most refer to implementing methods like the original one I had. And the GSON docs don't seem to cover this particular case. So again, how can I use Google GSON to deserialize a JSON array into a a Collection of a generic type?

Original source

Related problems