google / gson

A Java serialization/deserialization library to convert Java Objects into JSON and back
Apache License 2.0
23.38k stars 4.29k forks source link

NPE while invoke toJson() or fromJson() for HashMap<String, String> #1778

Open zewenwang opened 4 years ago

zewenwang commented 4 years ago

the exception:

java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Object.hashCode()' on a null object reference
  libcore.reflect.TypeVariableImpl.hashCode(TypeVariableImpl.java:47)
  java.util.HashMap.hash(HashMap.java:338)
  java.util.HashMap.containsKey(HashMap.java:595)
  java.util.HashSet.contains(HashSet.java:203)
  com.google.gson.internal.$Gson$Types.resolve(.java:346)
  com.google.gson.internal.$Gson$Types.resolve(.java:381)
  com.google.gson.internal.$Gson$Types.resolve(.java:337)
  com.google.gson.internal.$Gson$Types.getSupertype(.java:283)
  com.google.gson.internal.$Gson$Types.getCollectionElementType(.java:302)
  com.google.gson.internal.bind.CollectionTypeAdapterFactory.create(CollectionTypeAdapterFactory.java:52)
  com.google.gson.Gson.getAdapter(Gson.java:458)
  com.google.gson.Gson.toJson(Gson.java:696)
  com.google.gson.Gson.toJson(Gson.java:683)
  com.google.gson.Gson.toJson(Gson.java:638)
  com.google.gson.Gson.toJson(Gson.java:618)

my code:

new Gson().fromJson(json, new TypeToken<HashMap<String, String>>() {}.getType());

environment

Gson version 2.8.5 The probability of this problem happens substantial increases,when gson version upgrades from 2.8.2 to 2.8.5 !

Same problem with https://github.com/google/gson/issues/1298 , but not resolved.

zewenwang commented 4 years ago

@JakeWharton @Marcono1234 @inder123 @Degubi

Marcono1234 commented 4 years ago

Could this possibly be an Android bug (libcore.reflect.TypeVariableImpl is an Android class)? I assume Gson is not creating the TypeVariableImpl on its own but probably retrieves it somehow from a parameterized type, in that case Android would be the one creating that TypeVariableImpl which is causing the issues.

Could you please debug into TypeVariableImpl and check which expression is causing the NullPointerException?

fvdcx commented 4 years ago

I also met this issue, but my code calls "toJson" on a HashMap<String, Object>. @Marcono1234 @zewenwang how to escalate this bug?

Marcono1234 commented 4 years ago

As written in my comment above it would probably first be useful to get more information about which variable is null.

Aquarids commented 4 years ago

I also met this issue, but my code calls "toJson" on a HashMap<String, Object>. @Marcono1234 @zewenwang how to escalate this bug?

metoo, is there a fixed method?

Marcono1234 commented 4 years ago

As written above, it would be useful if you could provide more information:

Could this possibly be an Android bug (libcore.reflect.TypeVariableImpl is an Android class)? I assume Gson is not creating the TypeVariableImpl on its own but probably retrieves it somehow from a parameterized type, in that case Android would be the one creating that TypeVariableImpl which is causing the issues.

Could you please debug into TypeVariableImpl and check which expression is causing the NullPointerException?

Aquarids commented 4 years ago

As written above, it would be useful if you could provide more information:

Could this possibly be an Android bug (libcore.reflect.TypeVariableImpl is an Android class)? I assume Gson is not creating the TypeVariableImpl on its own but probably retrieves it somehow from a parameterized type, in that case Android would be the one creating that TypeVariableImpl which is causing the issues. Could you please debug into TypeVariableImpl and check which expression is causing the NullPointerException?

Sorry, here is the stacktrace.

libcore.reflect.TypeVariableImpl.hashCode(TypeVariableImpl.java:47)
java.util.HashMap.hash(HashMap.java:348)
java.util.HashMap.containsKey(HashMap.java:608)
java.util.HashSet.contains(HashSet.java:203)
com.google.gson.internal.$Gson$Types.resolve(.java:346)
com.google.gson.internal.$Gson$Types.resolve(.java:381)
com.google.gson.internal.$Gson$Types.resolve(.java:337)
com.google.gson.internal.$Gson$Types.getSupertype(.java:283)
com.google.gson.internal.$Gson$Types.getCollectionElementType(.java:302)
com.google.gson.internal.bind.CollectionTypeAdapterFactory.create(CollectionTypeAdapterFactory.java:52)
com.google.gson.Gson.getAdapter(Gson.java:458)
com.google.gson.Gson.toJson(Gson.java:696)
com.google.gson.Gson.toJson(Gson.java:683)
com.google.gson.Gson.toJson(Gson.java:638)

And my code is:

List<MyCustomClass> list = new ArrayList<>();
mGson.toJson(list);

or

List<MyCustomClass> list = new ArrayList<>();
mGson.toJson(list, TypeToken.getParameterized(List.class, MyCustomClass.class).getType());
Cyux07 commented 4 years ago

it looks like a NULL generic declaration of class cause NPE. https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/libcore/reflect/TypeVariableImpl.java

@Override
    public int hashCode() {
        return 31 * getName().hashCode() + getGenericDeclaration().hashCode();
    }

follow the trace, the toResolve's generic type lost?:

 private static Type resolve(Type context, Class<?> contextRawType, Type toResolve,
                              Collection<TypeVariable> visitedTypeVariables) {
    // this implementation is made a little more complicated in an attempt to avoid object-creation
    while (true) {
      if (toResolve instanceof TypeVariable) {
        TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
        if (visitedTypeVariables.contains(typeVariable)) {
          // cannot reduce due to infinite recursion
          return toResolve;
        } else {
          visitedTypeVariables.add(typeVariable);
        }
        toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
        if (toResolve == typeVariable) {
          return toResolve;
        }

      }......

And the toResolve come from $Gson$Types.getGenericSupertype(context, contextRawType, supertype).
Maybe this method return a NULL generic type collection in some case?

static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
    if (context instanceof WildcardType) {
      // wildcards are useless for resolving supertypes. As the upper bound has the same raw type, use it instead
      context = ((WildcardType)context).getUpperBounds()[0];
    }
    checkArgument(supertype.isAssignableFrom(contextRawType));
    return resolve(context, contextRawType,
        $Gson$Types.getGenericSupertype(context, contextRawType, supertype));
  }
Marcono1234 commented 3 years ago

Are you able to reproduce this exception when debugging on an emulator, or only for the deployed app? Which Android API Level does the target device have? Are you using a custom ProGuard configuration?

If possible could you please create a small example project which only contains the relevant code, which allows reproducing this issue (and describe reproduction steps if necessary).

Aquarids commented 3 years ago

Are you able to reproduce this exception when debugging on an emulator, or only for the deployed app? Which Android API Level does the target device have? Are you using a custom ProGuard configuration?

If possible could you please create a small example project which only contains the relevant code, which allows reproducing this issue (and describe reproduction steps if necessary).

Thanks for your reply, we have solved this problem by wrapping a JsonObject on the JsonArray. And this is an online occasional problem which reproted by our user, thus we also don't have more information to identify the reason of the problem.

EmMper commented 3 years ago

It happened to me on version 2.8.2, the same stack trace, and I also wrote the code below:

// Kotlin Code
Gson().fromJson(jsonString, object : TypeToken<List<MyCustomClass>>() {})

to deserialize with a type token, I think this may be a clue. And this problem won't happened every time, actually the frequency of problems is pretty low. So I'm not sure how to reproducing it. Now I'm trying to update Gson to the newest version 2.8.6, and I wonder will this issue happen again.

EmMper commented 3 years ago

Are you able to reproduce this exception when debugging on an emulator, or only for the deployed app? Which Android API Level does the target device have? Are you using a custom ProGuard configuration? If possible could you please create a small example project which only contains the relevant code, which allows reproducing this issue (and describe reproduction steps if necessary).

Thanks for your reply, we have solved this problem by wrapping a JsonObject on the JsonArray. And this is an online occasional problem which reproted by our user, thus we also don't have more information to identify the reason of the problem.

So your resolvent is to use another serialization tool? I noticed that you are in Beijing too, may I get your wechat or something to communicate with you?

Aquarids commented 3 years ago

Are you able to reproduce this exception when debugging on an emulator, or only for the deployed app? Which Android API Level does the target device have? Are you using a custom ProGuard configuration? If possible could you please create a small example project which only contains the relevant code, which allows reproducing this issue (and describe reproduction steps if necessary).

Thanks for your reply, we have solved this problem by wrapping a JsonObject on the JsonArray. And this is an online occasional problem which reproted by our user, thus we also don't have more information to identify the reason of the problem.

So your resolvent is to use another serialization tool? I noticed that you are in Beijing too, may I get your wechat or something to communicate with you?

没,我就直接用jsonObject又包了层,原本是[{"xxx", "yyy"}],现在是 {"data": [{"xxx", "yyy"}]}就暂时解决了。原因就和上面Cyux07说的一样。

EmMper commented 3 years ago

Are you able to reproduce this exception when debugging on an emulator, or only for the deployed app? Which Android API Level does the target device have? Are you using a custom ProGuard configuration? If possible could you please create a small example project which only contains the relevant code, which allows reproducing this issue (and describe reproduction steps if necessary).

Thanks for your reply, we have solved this problem by wrapping a JsonObject on the JsonArray. And this is an online occasional problem which reproted by our user, thus we also don't have more information to identify the reason of the problem.

So your resolvent is to use another serialization tool? I noticed that you are in Beijing too, may I get your wechat or something to communicate with you?

没,我就直接用jsonObject又包了层,原本是[{"xxx", "yyy"}],现在是 {"data": [{"xxx", "yyy"}]}就暂时解决了。原因就和上面Cyux07说的一样。

OK,了解了。但依然可以认为是Gson自己的bug吧?总感觉这样处理不能算是最终方案

Marcono1234 commented 2 years ago

In this pull request Gson users tried to solve this by modifying their ProGuard rules. Could you please try, with the latest version of Gson, whether disabling any shrinking and obfuscation for class com.google.gson.reflect.TypeToken and class * extends com.google.gson.reflect.TypeToken solves this issue?

Or in general make sure you apply the ProGuard rules shown in proguard.cfg, except for the one for com.google.gson.examples.android.model.**.

emanoellucasml commented 2 years ago

Hello, does anyone know if doing what @Marcono1234 suggested here the problem is solved? I am going through the same problem but updating the library version would affect many people, so I wanted to be sure before doing it.

Marcono1234 commented 2 years ago

I just mentioned using the latest Gson version to avoid having to debug issues in older versions, which might already be fixed in the latest version. Though this appears to be caused by ProGuard (or R8) so I assume adjusting the ProGuard rules without upgrading Gson might work as well.

vagrant1991 commented 1 year ago

https://issuetracker.google.com/issues/220725541

vagrant1991 commented 1 year ago

visit the above link, It sames like you have List, Map or Set (ex: ArrayList, HashSet) member in your java bean class, and not all of them using the @SerializedName annotation, so In R8 3.x, generics get stripped out, refer to : https://android.googlesource.com/platform/libcore/+/refs/heads/main/luni/src/main/java/libcore/reflect/TypeVariableImpl.java when you call hashcode method, getGenericDeclaration return null, so you get NullPointerException,

com.google.gson.internal.$Gson$Types.getCollectionElementType(.java:302)

take a look at getCollectionElementType in $Gson$Types, As the method name show you, It's all about Collections.