KurdTt-
KurdTt-

Reputation: 533

Java generic function to get JSON

I wanted to parse Objects from JSON and It works fine, but I wanted to generalize my function, to get every type I want. I tried to use generic type, but It gave me error.

My code:

//It works, but It's not universal
Type type = new TypeToken<List<WeatherParameter>>(){}.getType();
List<WeatherParameter> contactList = new Gson().fromJson(json, type);

//My function:
public static <T> List<T> getArrayFromJSON(String json) {
        Type type = new TypeToken<List<T>>(){}.getType();
        return new Gson().fromJson(json, type);
    }

Usage:
List<WeatherParameter> contactList = getArrayFromJSON(json);

//Error:
com.google.gson.internal.LinkedTreeMap cannot be cast to com.entity.model.WeatherParameter.

Why generic type T doesn't work in a TypeToken<List<T>>?

UPDATE

I've changed my function:

public static <T> List<T> getArrayFromJSON(String json, Class<T> clazz) {
        Type type = new TypeToken<List<T>>(){}.getType();
        List<T> list = new ArrayList<T>();
        List<Object> objectList = GSON.fromJson(json, type);
        for(Object o : objectList){
            list.add(clazz.cast(o));
        }
        return list;
    }

And it crashes on line list.add(clazz.cast(o));, error is the same: ClassCastException.

Full stack trace:

java.lang.ClassCastException: Cannot cast com.google.gson.internal.LinkedTreeMap to com.entity.model.WeatherParameter
    at java.lang.Class.cast(Class.java:3361)
    at com.server.utils.JsonUtils.getArrayFromJSON(JsonUtils.java:23)
    at com.server.task.ShowDataTask.process(ShowDataTask.java:21)
    at com.server.task.AbstractTask.run(AbstractTask.java:16)
    at com.server.utils.TaskUtils.lambda$doGet$1(TaskUtils.java:17)
    at com.server.utils.TaskUtils$$Lambda$22/18975860.handle(Unknown Source)
    at spark.RouteImpl$1.handle(RouteImpl.java:61)
    at spark.http.matching.Routes.execute(Routes.java:61)
    at spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:128)
    at spark.embeddedserver.jetty.JettyHandler.doHandle(JettyHandler.java:50)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:189)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at org.eclipse.jetty.server.Server.handle(Server.java:534)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
    at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
    at java.lang.Thread.run(Thread.java:745)

As I said, 23 line in JsonUtils: list.add(clazz.cast(o)); I call it this way:

List<WeatherParameter> weatherParameterList = JsonUtils.getArrayFromJSON(json, WeatherParameter.class);

Upvotes: 1

Views: 1099

Answers (1)

Tim Biegeleisen
Tim Biegeleisen

Reputation: 522007

The basic reason why your generic method is failing compilation is that after type erasure, the getArrayFromJSON() method returns a List<Object>, not a List<WeatherParameter>. Suppose that the compiler allowed your code to pass. Then it would be possible for you to assign a list of objects to list of weather parameters. But not every object is a weather parameter, so this might fail in a class cast exception.

One way to get around this would be to pass type information for the WeatherParameter class to your custom method, i.e.

public static <T> List<T> getArrayFromJSON(String json, Class<T> clazz) {
    // use class information somehow...
}

You might have a hard time since the class information would probably be needed inside the GSON library.

Have a look at the following question to get thinking about this:

How to have Java method return generic list of any type?

Upvotes: 4

Related Questions