Deserialize a List<T> object with Gson?
Solution 1
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.
Solution 2
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));
Solution 3
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);
Solution 4
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));
}
Solution 5
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);
jellyfish
Far, far away from my 10.000 hours, but I'm always willing to learn! :-) Atm working with Google Android.
Updated on December 19, 2021Comments
-
jellyfish over 2 years
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
andresult
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.
-
-
Drew over 12 yearsIf 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".
-
Pablo about 12 yearsIn 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?
-
Admin over 10 yearsUsing actual version of GSON (2.2.4) it works again. You can access the constructor here.
-
Amin Sh over 10 yearsthis is great! How can I use it with reflection? I dont know the
MyClass
value and it will be defined dynamically! -
CQM over 10 yearsmy 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 -
njzk2 about 10 yearsnota: with this, be careful that mcList is not a full-fledged list. many things will not work.
-
Happier over 9 yearsCheck the link to have another way to write a better util class. stackoverflow.com/questions/14139437/…
-
Pawel Cioch over 9 yearsHow to use it with generics?
T[] yourClassList = gson.fromJson(message, T[].class);
//cannot select from type variable -
Jose Ospina over 9 yearswhich package is neede for the type.getType() instruction? I am getting "The method getType() is undefined for the type new ArrayList<XXXX>(){}"
-
Al Lelopath about 9 yearsWhat is
mEntity.rulePattern
? -
Happier about 9 yearsIt's just a sample object for test. You don't need to care about it. Use toList method and everything goes well.
-
Pratik Butani about 9 yearsIt gives me error if i use proguard only otherwise its work. any suggestion
-
Umair Saleem almost 9 yearsFollowing Imports required --- import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken
-
jasxir almost 9 yearsThis is good if YourClass is fixed in code. What if the class comes at runtime?
-
Jan Rabe over 8 yearsthen you will most likely run into a class cast exception (java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap) when you try to use it
-
Rune about 8 yearsThe 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
-
Fermin Silva almost 8 yearsThose
{}
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? -
Mateus Viccari over 7 years@njzk2 What exactly is a full-fledged list?
-
njzk2 over 7 years@MateusViccari at the time of that comment,
mcList
in this answer was only the result of the call toArrays.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 actualArrayList
allows you to get a list that is more useful in many cases. -
JHH about 7 yearsSurely this cannot work since you're trying to use T in compile-time. This will effectively deserialize to a List of StringMap, no?
-
anshulkatta almost 7 yearsdont forget before Gson gson = new Gson(); for begineers
-
Lambart over 6 yearsGood one. But this duplicates
DevNG
s above answer, written 2 years earlier: stackoverflow.com/a/17300003/1339923 (and read that answer for caveats to this approach) -
coffeemakr over 6 yearsI've used your code to create this function using reified types:
inline fun <reified T> buildType() = object : TypeToken<T>() {}.type!!
and call it with the List type:buildType<List<YourMagicObject>>()
-
Chad Bingham over 6 years@coffeemakr You don't need reified types here.
-
coffeemakr over 6 yearsOh. But why do you create the type token of a ArrayList in
buildType
and also call the function with the generic type? Is this a typo? - This would create ArrayList<ArrayList<YourMagicObject>> -
Chad Bingham over 6 years@coffeemakr ah, yeah. Typo
-
Daniel Pryden over 6 yearsIf 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. -
Nikolay Kulachenko over 5 years
TypeToken#getParameterized
looks a way better then the hack with an anonymous subclass -
Steve Moretz about 5 years@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<Whatever> in that class,Now just use Whatever.class,For the second argument you're good to go.
-
illusionJJ about 5 yearsfor kotlin
val listType = object : TypeToken<ArrayList<YourClass>>() {}.type
-
leguminator over 4 yearsI copied your method "as is" and it does not work : compiler says "The method getParameterized(Class<List>, Class<T>) 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
-
Linh over 4 years@leguminator did you import TypeToken correct? and you are using java or kotlin. I will try to test again
-
leguminator over 4 years@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)"
-
4gus71n almost 4 yearsThis should be the accepted solution, simple, it uses the Gson API, and there are no hacks around it. +1