0

I need to parse a JSON array of objects into a list. I would pass

Class<?> classToParse 

as a parameter. According to Converting ZonedDateTime type to Gson, I need something like

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

to use with

 gsonInstance.fromJson(output, listType);

But how can I specify my "classToParse" (a parameter) instead of the hardcoded ExampleClass? I cannot insert it as is.

Community
  • 1
  • 1
onkami
  • 8,791
  • 17
  • 90
  • 176
  • You can't do that. But you can merely pass `TypeToken>(){}.getType()` instances and not `Class` instances for every particular `fromJson(...)` call. – Lyubomyr Shaydariv Feb 14 '17 at 13:50

1 Answers1

0

You cannot substitute the type parameter at the compile type with runtime invocations -- type information along with type parameters are generated by Java compiler at the compile time.

First off, note how Gson.fromJson(...) overloads are declared: neither uses TypeToken as a parameter, but they can accept Type -- this is fine, because TypeTokens are not mandatory to parse parameterized types JSONs, types matter only. Second, it's really fine to pass ready-to-use type Type instances to the fromJson method:

final Foo foo = gson.fromJson(..., Foo.class);
final List<Foo> foos = gson.fromJson(..., new TypeToken<List<Foo>>(){}.getType());

Note that in both cases literals are passed: the second one is just longer and created every time the line is executed. The Type interface does not provide any mutator methods, so you can consider it something that can be treated as a constant (as well as other value types ilke Strings, Integers, and so forth are):

static final TypeToken<List<Foo>> fooListTypeToken = new TypeToken<List<Foo>>(){};
...
final List<Foo> foos = gson.fromJson(..., fooListTypeToken.getType());

Or even shorter (however I prefer the previous way -- and I would recommend it to you [you can reuse the type token and not lose it]):

static final Type fooListType = new TypeToken<List<Foo>>(){}.getType();
...
final List<Foo> foos = gson.fromJson(..., fooListTypeToken);

How much fooListTypeToken and Foo.class differ from the programmatic point of view? Well, essentially no difference (except that the first one represents a list whilst the latter represents a concrete class, of course). Both are values.

However, if you want to get dynamic types by a given element class (or better a type) for any must reason, then you can create Type instances yourself and just pass them to fromJson:

static ParameterizedType createJavaUtilListParameterizedType(final Type elementType) {
    return new ParameterizedType() {
        @Override
        public Type getRawType() {
            return List.class;
        }

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

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

The method just creates an anonymous implementation of ParameterizedType where:

  • getRawType() - must return java.util.List.class, a container parameterized type
  • getActualTypeArguments() - must return the element type; note that it always must return a new array in order not to be damaged elsewhere (since it's not a 0-length array)
  • getOwnerType() - not implemented in this case and can safely return null.

So now the substitution is available at runtime:

final List<Foo> foos = gson.fromJson(..., createJavaUtilListParameterizedType(Foo.class));

but

final List<Foo> foos = gson.fromJson(..., createJavaUtilListParameterizedType(new TypeToken<GenericElement<String, Integer>>(){}.getType()));

for parameterized types. Well, still does not differ much from holding static final references to type tokens and extracting the types from them later. And semantically it's the same as what the compiler does at compile time.

Edit

Gson TypeToken also provides static TypeToken<?> getParameterized(Type rawType, Type... typeArguments) that can be used instead of own implementation of the parameterized type like:

static ParameterizedType createJavaUtilListParameterizedType(final Type elementType) {
    return (ParameterizedType) TypeToken.getParameterized(List.class, elementType).getType();
}
Lyubomyr Shaydariv
  • 20,327
  • 12
  • 64
  • 105