Home » Java » Google Gson – deserialize list<class> object? (generic type)

Google Gson – deserialize list<class> object? (generic type)

Posted by: admin November 2, 2017 Leave a comment

Questions:

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(){} 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!

Edit:

Now I have

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…?

Edit2:
I’v 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.

Edit3:
Solved, see my answer below.

Answers:

Method to deserialize generic collection:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();

List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

Import : import java.lang.reflect.Type;

Questions:
Answers:

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));

Questions:
Answers:

For those getting ambiguous Type import suggestions this is the one you want.

import java.lang.reflect.Type;

Questions:
Answers:

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));
}

Questions:
Answers:

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);

Questions:
Answers:
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, "[{...}]");

Questions:
Answers:

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.

Questions:
Answers:

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, 1);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add((T)array[i]);
    return list; 
}

Questions:
Answers:

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.

Questions:
Answers:

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);

Questions:
Answers:

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);

Questions:
Answers:

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());
    }
}

Questions:
Answers:

For Kotlin simply:

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

or, here is a useful function:

fun <T> buildType(): Type {
    return object : TypeToken<ArrayList<T>>() {}.type
}

Then, to use:

val type = buildType<ArrayList<YourMagicObject>>()