user2015253
user2015253

Reputation: 1444

Find out if GSON's TypeToken contains a List?

I'm using GSON to parse json in Java. When I want to get an integer list from a json String, my code may look like this:

    Type type = new TypeToken<List<Integer>>(){}.getType();

    List<Integer> result = null;
    try {
        result = new Gson().fromJson(jsonStringGoesHere, type);
    } catch (JsonSyntaxException e) {
        e.printStackTrace();
    }

If jsonStringGoesHere is an empty String or null, result is also null.

It is considered bad practice to have an Collection equal null though, so I would prefer to have an empty List instead of null for this case.

So basically this is what I want to do now: If result is null, I want to check if type is a List (or a subtype of List). If true, I'll make result an empty ArrayList. How do I check whether type contains a List or subtype of List though?

My attempt was checking if(type instanceof Collection) - but that returns false.

My next try was to take type.getTypeName() which returns the String java.util.List<java.lang.Integer>, and tried to create an object from that:

        Class c = Class.forName(type.getTypeName());
        Object o = c.newInstance();
        if(o instanceof List){
            System.out.println("it's working!");
        }

But this throws java.lang.ClassNotFoundException: java.util.List<java.lang.Integer>.

Any ideas how to make this work?

Upvotes: 2

Views: 1814

Answers (2)

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21125

My attempt was checking if(type instanceof Collection) - but that returns false.

This won't work because Type and Collection are not "mutually castable" and exist in different hierarchies.

My next try was to take type.getTypeName() which returns the String java.util.List<java.lang.Integer>

This won't work either. Class.forName returns an existing Class<?> instance by a fully qualified name (simply speaking, something that's represented by a .class file in CLASSPATH), but never -- a parameterized class (classes are not parameterizable but can be subclasses with parameterized superclasses -- this is what's under the hood of type tokens). Thus, Class.forName("java.util.List") makes sense, but Class.forName("java.util.List<String>") does not.

Note that Type is something that can represent a type, and TypeToken<T> serves this purpose. Just take a look at how it's declared -- and you'll understand it better. Even more, its subinterface ParameterizedType can hold type parameters, and this this can be generated either by TypeToken or constructed manually (as usual -- just take a look at the interface declaration). TypeTokens usually use anonymous base class parameterization to take the type parameters from, and that's why they are designed like that: well-done compile-time support + there are similar techniques in Google Guava, Jackson, Spring Framework (but it would be nice if Java could support something like java.util.List<Integer>.type to get a parameterized type instance). As of later Gson versions, you can even use TypeToken.getParameterized(...) that is simply a convenience method to create a parameterized type.

What you're looking for is, a "class-instanceof" or "a-subclass-of" (if it's fine to call it like that): Class.isAssignableFrom(...). This instance method checks if one class is the same or a subclass of a another class. Say,

SuperClass.class.isAssignableFrom(SubClass.class)

always returns true. So you only have to check like this:

final Type type = new TypeToken<List<Integer>>() {
}.getType();
final TypeToken<?> typeToken = TypeToken.get(type);
System.out.println(List.class.isAssignableFrom(typeToken.getRawType()));

Or even shorter:

final TypeToken<List<Integer>> typeToken = new TypeToken<List<Integer>>() {
};
System.out.println(List.class.isAssignableFrom(typeToken.getRawType()));

Both return true. Note that type tokens return Class<?> instances via getRawType(), but not getType() since the latter may represent not a class (say, what would Collection<String>.class.isAssignableFrom(List<Integer.class>) return?)

Also, you can extract type tokens and its types to static final fields since they are meant to be immutable values and should not change in runtime.

Upvotes: 1

Nishesh Pratap Singh
Nishesh Pratap Singh

Reputation: 2181

In Java Generic part of collection comes into play at compile time, and therefore it is not available at runtime and that is the reason you are getting that exception. So if you want to create class of Collection type using reflection then you have to remove the generic part.

Also List,Set and Map are interfaces they can not be instantiated directly. So in order to create an object you have to use one of their implementation, like ArrayList,LinkedList etc.

Upvotes: 0

Related Questions