50

I knew this question has been asked before. Due to my novice skill in java and android. I can't resolve this issue for more than a week.

One of my friend and i developing an android project were there are a couple of things like this.

The most weird part of this things is, it's happening only from when i download and test it from Google play store. Not from local android studio installation or debug mode.

What could be the problem here, or this returning list which is totally wrong ? My friend convincing that this code returns correctly but from play store installation it's always an error.

Please suggest me where should i keep digging?

@Override
public void promiseMethod(JSONObject object) {
    if (object != null) {

        if (object.has(DO_SERVICES)) {
            vehicleDetails = new ArrayList < Object[] > (1);
            List < String > vehicleNoList = new ArrayList < String > (1);
            List < String > serviceList = new ArrayList < String > (1);
            try {
                Gson gson = new Gson();
                JSONObject jsonObj = new JSONObject(object.get(DO_SERVICES)
                    .toString());
                servDto = gson.fromJson(jsonObj.toString(),
                    ServiceDto.class);


                if (servDto.getServiceDto() instanceof List) {

                    List < DoServiceDto > doServiceList = servDto.getServiceDto();

Exception is

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.gaurage.dto.DoServiceDto at com.gaurage.user.User_Test_Main.promiseMethod(Unknown Source)

iagreen
  • 31,470
  • 8
  • 76
  • 90
EngineSense
  • 3,266
  • 8
  • 28
  • 44
  • Weird problem for more than week... it works in local installation not from the play store installation – EngineSense Sep 09 '15 at 05:49
  • @CodeSpy btw, thanks for providing great answer and approach but using it's recommended to use `TypeToken` instead manually get the `LinkedMapTree` object by name – mochadwi Feb 11 '20 at 06:55

6 Answers6

123

Serializing and Deserializing Generic Types

When you call toJson(obj), Gson calls obj.getClass() to get information on the fields to serialize. Similarly, you can typically pass MyClass.class object in the fromJson(json, MyClass.class) method. This works fine if the object is a non-generic type. However, if the object is of a generic type, then the Generic type information is lost because of Java Type Erasure. Here is an example illustrating the point:

class Foo<T> {  T value;}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // May not serialize foo.value correctly
gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar

The above code fails to interpret value as type Bar because Gson invokes list.getClass() to get its class information, but this method returns a raw class, Foo.class. This means that Gson has no way of knowing that this is an object of type Foo, and not just plain Foo.

You can solve this problem by specifying the correct parameterized type for your generic type. You can do this by using the TypeToken class.

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();    
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);

I have Parent class and it's child class some of them having List types in it. Like this parent class i have 30 files. I solved it like this.

Gson gson = new Gson();
JSONObject jsonObj = new JSONObject(object.get(DO_SERVICES).toString());
Type type = new TypeToken<MyDto>() {}.getType();
servDto = gson.fromJson(jsonObj.toString(),type);

The most important thing is, I can't reproduce this error in local testing from Android studio. This problem pops up only, When i generate signed apk and publish app into PlayStore were the app stops, and the report says Cannot cast LinkedTreeMap to myclass.

It was hard for me to reproduce the same result in my local testing (includes Debug mode).

Saidolim
  • 379
  • 10
  • 20
EngineSense
  • 3,266
  • 8
  • 28
  • 44
  • 6
    reproduces when using proguard (minifyEnabled true) – Pnemonic Jan 09 '17 at 15:13
  • 1
    @Log.d Thanks, that worked for me. It took 6 hours before I realised the issue and came across your post. – Hanu May 26 '17 at 06:23
  • @Saidolim what is meant by object.get(DO_SERVICES) in your answer ? JSONObject jsonObj = new JSONObject(object.get(DO_SERVICES).toString()); – Krish Oct 24 '17 at 20:42
  • @Krishna I don't know whom you are pointing at. But Do_service is a string from rest api. It's an old method since I moved to hibernate. – EngineSense Oct 25 '17 at 02:36
9

EngineSense's answer is correct.
However, if you still want to use generics and don't want to pass in the concrete class type as a parameter here's an example of a workaround in Kotlin.
(Note that inline methods with reified type params cannot be called from Java).

May not be the most efficient way to get things done but it does work.


The following is in GsonUtil.kt

inline fun <reified T> fromJson(json: String): T? {
    return gson.fromJson(json, object : TypeToken<T>() {}.type)
}

fun <T> mapToObject(map: Map<String, Any?>?, type: Class<T>): T? {
    if (map == null) return null

    val json = gson.toJson(map)
    return gson.fromJson(json, type)
}

Method that retrieves a lightweight list of generic objects.

inline fun <reified T: MyBaseClass> getGenericList(): List<T> {
    val json = ...

    //Must use map here because the result is a list of LinkedTreeMaps
    val list: ArrayList<Map<String, Any?>>? = GsonUtil.fromJson(json)
    //handle type erasure
    val result = list?.mapNotNull {
        GsonUtil.mapToObject(it, T::class.java)
    }

    return  result ?: listOf()
}
Markymark
  • 2,804
  • 1
  • 32
  • 37
8

Source

In My ListView BaseAdapter facing same issue

JSON format to show

{
    results: [
    {
        id: "10",
        phone: "+91783XXXX345",
        name: "Mr Example",
        email: "freaky@jolly.com"
    },
    {
        id: "11",
        phone: "+9178XXXX66",
        name: "Mr Foo",
        email: "freaky@jolly.com"
    }],
    statusCode: "1",
    count: "2"
}

I was facing this issue

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.hsa.ffgp.hapdfgdfgoon, PID: 25879 java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.hsa.......

Then I mapped data using LinkedTreeMap Key Value as below

...
...

    @Override
    public View getView(final int i, View view, ViewGroup viewGroup) {
        if(view==null)
        {
            view= LayoutInflater.from(c).inflate(R.layout.listview_manage_clients,viewGroup,false);
        }

        TextView mUserName = (TextView) view.findViewById(R.id.userName);
        TextView mUserPhone = (TextView) view.findViewById(R.id.userPhone);


        Object getrow = this.users.get(i);
        LinkedTreeMap<Object,Object> t = (LinkedTreeMap) getrow;
        String name = t.get("name").toString();

        mUserName.setText("Name is "+name);
        mUserPhone.setText("Phone is "+phone);

        return view;
    }
...
...
Code Spy
  • 9,626
  • 4
  • 66
  • 46
4
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

String json = new Gson().toJson("Your Value");
ArrayList<YourClassName> outputList = new Gson().fromJson("Receive Value", new TypeToken<ArrayList<YourClassName>>() {
            }.getType());

Log.d("TAG", outputList.get(0).getName);
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
2

In my case error occurred while fetching a list of objects from shared preferences. The error was solved by adding TypeToken as shown below:

 public static <GenericClass> GenericClass getListOfObjectsFromSharedPref(Context context,String preferenceFileName, String preferenceKey ){

    SharedPreferences sharedPreferences = 
    context.getSharedPreferences(preferenceFileName, 0);
    Type type = new TypeToken<ArrayList<Classname.class>>() {}.getType();
    String json = sharedPreferences.getString(preferenceKey, "");
    final Gson gson = new Gson();
    return gson.fromJson(json, type);
}

....

Deepak Kataria
  • 554
  • 3
  • 10
0

For me, it was due to minifyEnabled = true, if you enabled it, please add the class you are trying to parse in the progaurd-rules.pro file.

-keep class your.pakage.YourClassName
Arun P M
  • 352
  • 1
  • 15