Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

com.google.gson.internal.LinkedTreeMap cannot be cast to my class [duplicate]

I have some problems with getting my object from a JSON string.

I got the class Product

public class Product {
    private String mBarcode;
    private String mName;
    private String mPrice;

    public Product(String barcode, String name, String price) {
        mBarcode = barcode;
        mName = name;
        mPrice = price;
    }

    public int getBarcode() {
        return Integer.parseInt(mBarcode);
    }

    public String getName() {
        return mName;
    }

    public double getPrice() {
        return Double.parseDouble(mPrice);
    }
}    

From my server I get an ArrayList<Product> in JSON String representation. For example:

[{"mBarcode":"123","mName":"Apfel","mPrice":"2.7"},
{"mBarcode":"456","mName":"Pfirsich","mPrice":"1.1111"},
{"mBarcode":"89325982","mName":"Birne","mPrice":"1.5555"}] 

This String is generated like this:

public static <T> String arrayToString(ArrayList<T> list) {
    Gson g = new Gson();
    return g.toJson(list);
}

To get my Object back I use this function:

public static <T> ArrayList<T> stringToArray(String s) {
    Gson g = new Gson();
    Type listType = new TypeToken<ArrayList<T>>(){}.getType();
    ArrayList<T> list = g.fromJson(s, listType);
    return list;
}

But when calling

String name = Util.stringToArray(message).get(i).getName();

I get the error com.google.gson.internal.LinkedTreeMap cannot be cast to object.Product

What am I doing wrong? It looks like it created a List of LinkedTreeMaps but how do i convert those into my Product Object?

like image 201
Tanzmaus Avatar asked Dec 02 '14 15:12

Tanzmaus


3 Answers

In my opinion, due to type erasure, the parser can't fetch the real type T at runtime. One workaround would be to provide the class type as parameter to the method.

Something like this works, there are certainly other possible workarounds but I find this one very clear and concise.

public static <T> List<T> stringToArray(String s, Class<T[]> clazz) {
    T[] arr = new Gson().fromJson(s, clazz);
    return Arrays.asList(arr); //or return Arrays.asList(new Gson().fromJson(s, clazz)); for a one-liner
}

And call it like:

String name = stringToArray(message, Product[].class).get(0).getName();
like image 187
Alexis C. Avatar answered Nov 19 '22 19:11

Alexis C.


Similar to Alexis C's answers. but in Kotlin.
Just pass the class type into function and clarify what generic type is.
Here is simplified example.

inline fun <reified T> parseArray(json: String, typeToken: Type): T {
    val gson = GsonBuilder().create()
    return gson.fromJson<T>(json, typeToken)
}

Here is example call

fun test() {
    val json: String = "......."
    val type = object : TypeToken<List<MyObject>>() {}.type
    val result: List<MyObject> = parseArray<List<MyObject>>(json = json, typeToken = type)
    println(result)
}
like image 23
Johnny Avatar answered Nov 19 '22 18:11

Johnny


I also had problems with GSON complaining about casting LinkedTreeMaps.

The answer provided by Alexis and the comment by Aljoscha explains why the error occurs; "Generics on a type are typically erased at runtime." My issue was that my code worked when I ran it normally, but using ProGuard caused code to be stripped that was vital to casting.

You can follow Alexis's answer and more clearly define the cast and that should fix the problems. You can also add the ProGuard rules given by Google (simply doing this cleared the issue up for me).

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }

##---------------End: proguard configuration for Gson  ----------

Moral of the Story: Always check to see what ProGuard rules you need.

like image 19
welshk91 Avatar answered Nov 19 '22 17:11

welshk91