529

I want to transfer a list object via Google Gson, but I don't know how to deserialize generic types.

What I tried after looking at this (BalusC's answer):

MyClass mc = new Gson().fromJson(result, new List<MyClass>() {}.getClass());

but then I get an error in Eclipse saying "The type new List<MyClass>() {} must implement the inherited abstract method..." and if I use a quick fix I get a monster of over 20 method stubs.

I am pretty sure that there is an easier solution, but I seem unable to find it!

Now I have this:

Type listType = new TypeToken<List<MyClass>>() {}.getType();

MyClass mc = new Gson().fromJson(result, listType);

However, I do get the following exception at the fromJson line:

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

I do catch JsonParseExceptions and result is not null.

I checked listType with the debugger and got the following:

  • list Type
    • args = ListOfTypes
      • list = null
      • resolvedTypes = Type[ 1 ]
    • loader = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = Class (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

So it seems the getClass invocation didn't work properly. Any suggestions...?

I've checked on the Gson User Guide. It mentions a runtime exception that should happen during parsing a generic type to Json. I did it "wrong" (not shown above), just as in the example, but didn't get that exception at all. So I changed the serialization as in the user guide suggested. Didn't help, though.

Edit:

Solved, see my answer below.

Lii
  • 11,553
  • 8
  • 64
  • 88
jellyfish
  • 7,868
  • 11
  • 37
  • 49

15 Answers15

1101

Method to deserialize generic collection:

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

Since several people in the comments have mentioned it, here's an explanation of how the TypeToken class is being used. The construction new TypeToken<...>() {}.getType() captures a compile-time type (between the < and >) into a runtime java.lang.reflect.Type object. Unlike a Class object, which can only represent a raw (erased) type, the Type object can represent any type in the Java language, including a parameterized instantiation of a generic type.

The TypeToken class itself does not have a public constructor, because you're not supposed to construct it directly. Instead, you always construct an anonymous subclass (hence the {}, which is a necessary part of this expression).

Due to type erasure, the TypeToken class is only able to capture types that are fully known at compile time. (That is, you can't do new TypeToken<List<T>>() {}.getType() for a type parameter T.)

For more information, see the documentation for the TypeToken class.

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
uncaught_exceptions
  • 21,712
  • 4
  • 41
  • 48
  • 34
    In new versions of GSON the TypeToken contructor is not public, hence here you get constructor not visible error. What do you have to do in this case? – Pablo Apr 03 '12 at 20:09
  • 8
    Using actual version of GSON (2.2.4) it works again. You can access the constructor here. –  Nov 20 '13 at 22:42
  • my json object starts with an object, then contains an array of the object I want `{ "myObjectArray":[ {....} , {....} , {....} ] }`, I have made the model file for `{....}` , how do I get this generic collection code to not assume my root element is an array without making a new nested object file – CQM Feb 06 '14 at 21:02
  • Check the link to have another way to write a better util class. http://stackoverflow.com/questions/14139437/java-type-generic-as-argument-for-gson – Happier Jan 26 '15 at 08:38
  • which package is neede for the type.getType() instruction? I am getting "The method getType() is undefined for the type new ArrayList(){}" – Jose Ospina Mar 01 '15 at 17:09
  • It gives me error if i use proguard only otherwise its work. any suggestion – Pratik Butani May 07 '15 at 09:48
  • 9
    Following Imports required --- import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken – Umair Saleem Jun 30 '15 at 11:38
  • 5
    This is good if YourClass is fixed in code. What if the class comes at runtime? – jasxir Sep 08 '15 at 08:40
  • then you will most likely run into a class cast exception (java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap) when you try to use it – Jan Rabe Nov 09 '15 at 15:47
  • Those `{}` before `.getType` look so non-Java to me. I'm not even able to tell what they are syntacically. Is it an empty anonymous class or what? – Fermin Silva Aug 04 '16 at 13:43
  • @CQM You have two ways.1-Just remove the first and the last character from the string and you're good to go.2-Make a Class put your ArrayList in that class,Now just use Whatever.class,For the second argument you're good to go. – Steve Moretz Mar 27 '19 at 06:42
  • for kotlin `val listType = object : TypeToken>() {}.type` – illusionJJ Mar 27 '19 at 06:52
  • 1
    @jasxir, if the type argument is dynamic and not known at compile time, use `TypeToken.getParameterized`, as shown in [this answer](https://stackoverflow.com/a/51943312). – Marcono1234 Sep 13 '22 at 19:12
307

Another way is to use an array as a type, e.g.:

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

This way you avoid all the hassle with the Type object, and if you really need a list you can always convert the array to a list by:

List<MyClass> mcList = Arrays.asList(mcArray);

IMHO this is much more readable.

And to make it be an actual list (that can be modified, see limitations of Arrays.asList()) then just do the following:

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
DevNG
  • 5,875
  • 3
  • 18
  • 14
  • 4
    this is great! How can I use it with reflection? I dont know the `MyClass` value and it will be defined dynamically! – Amin Sh Dec 27 '13 at 16:10
  • 2
    nota: with this, be careful that mcList is not a full-fledged list. many things will not work. – njzk2 Jun 05 '14 at 13:29
  • 4
    How to use it with generics? `T[] yourClassList = gson.fromJson(message, T[].class);` //cannot select from type variable – Pawel Cioch Feb 21 '15 at 17:31
  • @njzk2 What exactly is a full-fledged list? – Mateus Viccari Mar 05 '17 at 18:36
  • 3
    @MateusViccari at the time of that comment, `mcList` in this answer was only the result of the call to `Arrays.asList`. This method returns a list on which most if not all optional methods are left unimplemented and throw exceptions. For instance, you cannot add any element to that list. As the later edit suggests, `Arrays.asList` has limitations, and wrapping it into an actual `ArrayList` allows you to get a list that is more useful in many cases. – njzk2 Mar 06 '17 at 04:00
  • 1
    dont forget before Gson gson = new Gson(); for begineers – anshulkatta Sep 02 '17 at 13:48
  • 2
    If you need to construct an array type at runtime for an arbitrary element type, you can use `Array.newInstance(clazz, 0).getClass()` as described in [David Wood's answer](https://stackoverflow.com/questions/5554217/google-gson-deserialize-listclass-object-generic-type/36338377#36338377). – Daniel Pryden Mar 20 '18 at 12:24
86

Since Gson 2.8, we can create util function like this:

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

Example usage:

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);
Lii
  • 11,553
  • 8
  • 64
  • 88
Linh
  • 57,942
  • 23
  • 262
  • 279
  • 3
    `TypeToken#getParameterized` looks a way better then the hack with an anonymous subclass – Nikolay Kulachenko Oct 10 '18 at 14:46
  • 1
    I copied your method "as is" and it does not work : compiler says "The method getParameterized(Class, Class) is undefined for the type TypeToken". I checked both my Gson version (2.8.0) and documentation and everything is fine on this side... I ended up using @Happier solution which works fine – leguminator Feb 20 '20 at 09:19
  • @leguminator did you import TypeToken correct? and you are using java or kotlin. I will try to test again – Linh Feb 20 '20 at 10:00
  • @PhanVanLinh Absolutely : I am using Java and imported "com.google.gson.reflect.TypeToken" and "java.lang.reflect.Type". I doubled check method implementation : it is declared as "public static TypeToken> getParameterized(Type rawType, Type... typeArguments)" – leguminator Feb 20 '20 at 15:49
  • 1
    This should be the accepted solution, simple, it uses the Gson API, and there are no hacks around it. +1 – 4gus71n Aug 29 '20 at 18:21
  • 2
    For what it's worth the solution using an anonymous `TypeToken` subclass is not really a hack, but the officially recommended usage by Gson (see its Javadoc and the [User Guide](https://github.com/google/gson/blob/master/UserGuide.md#TOC-Serializing-and-Deserializing-Generic-Types)). The main advantage is that it makes sure you construct proper parameterized types. `TypeToken.getParameterized` cannot perform any validation at compile time so you could construct types such as `List` or `Map`; the latest Gson versions at least detect these bad types at runtime. – Marcono1234 Sep 13 '22 at 19:18
  • 1
    @Marcono1234 Another advantage of anonymous TypeToken is shown when we need nested generics. For instance while parsing `[[1,2],[3,4],[5,6]]` I would rather read/maintain code with `new TypeToken>(){}.getType()` than `TypeToken.getParameterized(List.class, TypeToken.getParameterized(List.class, Integer.class).getType()).getType()`. – Pshemo Sep 19 '22 at 14:13
32

Refer to this post. Java Type Generic as Argument for GSON

I have better solution for this. Here's the wrapper class for list so the wrapper can store the exactly type of list.

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

  @Override
  public Type[] getActualTypeArguments()
  {
      return new Type[] { wrapped };
  }

  @Override
  public Type getRawType()
  {
    return List.class;
  }

  @Override
  public Type getOwnerType()
  {
    return null;
  }
}

And then, the code can be simple:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}
Community
  • 1
  • 1
Happier
  • 838
  • 8
  • 21
28

Wep, another way to achieve the same result. We use it for its readability.

Instead of doing this hard-to-read sentence:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

Create a empty class that extends a List of your object:

public class YourClassList extends ArrayList<YourClass> {}

And use it when parsing the JSON:

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);
Roc Boronat
  • 11,395
  • 5
  • 47
  • 59
13

For Kotlin simply:

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<List<T>>() {}.type

or, here is a useful function:

fun <T> typeOfList(): Type {
    return object : TypeToken<List<T>>() {}.type
}

Then, to use:

val type = typeOfList<YourMagicObject>()
Vlad
  • 7,997
  • 3
  • 56
  • 43
Chad Bingham
  • 32,650
  • 19
  • 86
  • 115
7
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

Example:

getList(MyClass[].class, "[{...}]");
kayz1
  • 7,260
  • 3
  • 53
  • 56
  • Good one. But this duplicates `DevNG`s above answer, written 2 years earlier: https://stackoverflow.com/a/17300003/1339923 (and read that answer for caveats to this approach) – Lambart Sep 27 '17 at 19:23
6

Here is a solution that works with a dynamically defined type. The trick is creating the proper type of of array using Array.newInstance().

public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}
Lii
  • 11,553
  • 8
  • 64
  • 88
David Wood
  • 434
  • 1
  • 5
  • 14
6

As it answers my original question, I have accepted doc_180's answer, but if someone runs into this problem again, I will answer the 2nd half of my question as well:

The NullPointerError I described had nothing to do with the List itself, but with its content!

The "MyClass" class didn't have a "no args" constructor, and neither had its superclass one. Once I added a simple "MyClass()" constructor to MyClass and its superclass, everything worked fine, including the List serialization and deserialization as suggested by doc_180.

jellyfish
  • 7,868
  • 11
  • 37
  • 49
  • 1
    If you have a list of abstract classes you'll get the same error. I guess this is GSON's general error message for "Unable to instantiate class". – Drew Sep 30 '11 at 01:18
  • The tip about adding a constructor helped me realize why I had all null-values. I had field names like "To" and "From" in my JSON-string, but the corresponding fields in my object were "to" and "from" in lower case, so they were skipped – Rune Jun 19 '16 at 08:03
3

I want to add for one more possibility. If you don't want to use TypeToken and want to convert json objects array to an ArrayList, then you can proceed like this:

If your json structure is like:

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

and your class structure is like:

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

then you can parse it like:

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

Now you can access each element of className object.

Apurva Sharma
  • 191
  • 2
  • 7
1

Refer to example 2 for 'Type' class understanding of Gson.

Example 1: In this deserilizeResturant we used Employee[] array and get the details

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

Example 2:

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}
Shashanth
  • 4,995
  • 7
  • 41
  • 51
Ram Patro
  • 121
  • 4
  • 10
1

using Kotlin, you can get generic MutableList type for all custom Serializable Types

private fun <T : Serializable> getGenericList(
    sharedPreferences: SharedPreferences,
    key: String,
    clazz: KClass<T>
): List<T> {
    return sharedPreferences.let { prefs ->
        val data = prefs.getString(key, null)
        val type: Type = TypeToken.getParameterized(MutableList::class.java, clazz.java).type
        gson.fromJson(data, type) as MutableList<T>
    }
}

you can call this function

getGenericList.(sharedPrefObj, sharedpref_key, GenericClass::class)
0

In My case @uncaught_exceptions's answer didn't work, I had to use List.class instead of java.lang.reflect.Type:

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
Andrei
  • 801
  • 2
  • 10
  • 27
0

I have created GsonUtils lib for this case. I add this into maven central repository.

Map<String, SimpleStructure> expected = new HashMap<>();
expected.put("foo", new SimpleStructure("peperoni"));

String json = GsonUtils.writeValue(expected);

Map<String, SimpleStructure> actual = GsonUtils.readMap(json, String.class, SimpleStructure.class);
Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
-1

I liked the answer from kays1 but I couldn't implement it. So I built my own version using his concept.

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

Usage:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);
spenibus
  • 4,339
  • 11
  • 26
  • 35
mike83_dev
  • 481
  • 1
  • 6
  • 11
  • Surely this cannot work since you're trying to use T in compile-time. This will effectively deserialize to a List of StringMap, no? – JHH Apr 04 '17 at 13:55