Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arraylist is of type LinkedTreeMap instead of Brewer

Tags:

java

android

gson

I am trying to make my response class generic. The problem is that it returns a List with the type com.google.gson.internal.LinkedTreeMap instead of the type Brewer.

The function that starts all this:

List<Brewer> brewerList = null;
try {
    Response<Brewer> responseHandler = new Response<Brewer>();
    brewerList = responseHandler.getAll(response);
    functionCallBack = Constants.FUNCTION_DB_BREWER;
    list = brewerList.toArray(new Brewer[brewerList.size()]);
} catch (IOException e) {
    Tools.LOG_ERROR(e);
}

This is my response class:

public class Response<T> {

public List<T> getAll(JSONObject response) throws IOException {
    List<T> brewerList = null;

    // Map JSON to JAVA Objects
    Type listResponse = new TypeToken<ListResponse<T>>() {
    }.getType();
    ListResponse<T> responseObject = Shared.gson.fromJson(response.toString(), listResponse);

    if (responseObject != null) {
        // Status, Message, Data
        int status = responseObject.getStatus();

        Tools.LOG_DEBUG("Response - getAll, Status: " + status);

        if (responseObject.getData() != null && responseObject.getStatus() == 200) {
            brewerList = responseObject.getData();
        } else {
            Tools.LOG_DEBUG("Couldn't find the webservices!");
        }
    }
    return brewerList;
}
}

And this is my data class that i give to parse from json

public class ListResponse<T> {

private int status;
private List<T> data;

public ListResponse() {
}

public int getStatus() {
    return status;
}

public void setStatus(int status) {
    this.status = status;
}

public List<T> getData() {
    return data;
}

public void setData(List<T> data) {
    this.data = data;
}
}

Exception:

04-11 10:54:55.994  31660-31660/be.appmax.ktsjjt E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.ArrayStoreException: source[0] of type com.google.gson.internal.LinkedTreeMap cannot be stored in destination array of type be.appmax.ktsjjt.models.Brewer[]
        at java.lang.System.arraycopy(Native Method)
        at java.util.ArrayList.toArray(ArrayList.java:523)
        at be.appmax.ktsjjt.database.SyncHandler.onRestTaskCompleted(SyncHandler.java:96)
        at be.appmax.ktsjjt.webservices.WSBrewer$1.onSuccess(WSBrewer.java:36)
        at com.loopj.android.http.JsonHttpResponseHandler$1$1.run(JsonHttpResponseHandler.java:125)
        at android.os.Handler.handleCallback(Handler.java:730)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:176)
        at android.app.ActivityThread.main(ActivityThread.java:5419)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:525)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
        at dalvik.system.NativeStart.main(Native Method)
like image 515
kevingoos Avatar asked Apr 11 '14 09:04

kevingoos


1 Answers

Unfortunately you can't do what you're trying to do because of type erasure in Java.

The TypeToken hack used by various libraries that do deserialization tries to get around this, but you can't use it with a generic type parameter as you're attempting to do. The type of T is erased at runtime and Gson can no longer determine the actual type so ... it returns a Map.

Edit to add: The way to get around this is to pass in the TypeToken rather than trying to create it in the method. This allows the type to be known. The tricky bit is in your case you want to return a List<T> but the TypeToken is actually ListResponse<T>. Because of that you have to get a bit creative with Generics and infer the TypeToken type with a bounding:

public <V extends ListResponse<T>> List<T> getAll(JSONObject response, TypeToken<V> token) throws IOException {
    ...
    V responseObject = 
        Shared.gson.fromJson(response.toString(), token.getType());
    ...
}

When you call this, you'll need to pass in the TypeToken instance, but then it will work.

Response<Brewer> responseHandler = new Response<Brewer>();
TypeToken<ListResponse<Brewer>> token = new TypeToken<ListResponse<Brewer>>(){};
brewerList = responseHandler.getAll(response, token);
like image 150
Brian Roach Avatar answered Oct 15 '22 07:10

Brian Roach