Gson.toJson returns null, ProGuard issue

Mr.AF :

users Bug report shows Gson().toJson(obj) occasionally returns {} but for most users it works correct.

i have visited an user who faced the bug and debugged app on his phone and i made Toast to show what is sending to server and i saw Toast shows {} and also Records and ID aren't null.

here is what i have done.

private class ClassA{
        String ID;
        ArrayList<ClassB> Records;

        ClassA(String ID, ArrayList<ClassB> Records) {
            this.ID= ID;
            this.Records= Records;
        }
 }

 private class ClassB {
        int T;
        int F;
        String D;

        ClassB (int T, int F, String D) {
            this.T= T;
            this.F = F;
            this.D= D;
        }

}

And here i do serialize object

ClassA obj = new ClassA(ID,Records); 
String json = new Gson().toJson(obj);

but new Gson().toJson(obj) for some users works correct but for some return {}

Server database shows some users sent data {} but some correct json .after some research i found new Gson().toJson(obj) returns {}.no webservice problem and no database problem.

Extra info

ArrayList<ClassB> Records= new ArrayList<>();
Records.add(new ClassB(Integer.valueOf(t.toString()), Integer.valueOf(f.toString()),d.toString()));
ClassA obj = new ClassA(ID,Records); 
String json = new Gson().toJson(obj);

Database

id    | data
----------------
c89   | {"ID":"c89","Records":[{"T":0,"F":0,"D":"2019/04/11 05:48 PM"}]} correct one

c90   | {} bug

Null Input Test

i did below test and i found problem is beyond the null inputs

ArrayList<ClassB> Records= new ArrayList<>();
ClassA obj = new ClassA(null,Records);    
Toast.makeText(getApplicationContext(),new Gson().toJson(obj ),Toast.LENGTH_LONG).show();

Toast shows {"Records":[]}.worse than upper condition never happens

And also IDE says

if(ID!=null && Records!=null) <= this condition is always true 
ClassA obj = new ClassA(ID,Records); 

proguard-rules.pro

##---------------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
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

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

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

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

How do i make it to work correct for all users?

Comments

This shouldn't be possible, you need more details on the {} case e.g. is it consistent, is it due to multi-threading, is it a specific JDK version

There isn't any multi-threading phenomena.for example, no Runnable no AsycTask no Thread.it's just a normal fragment which gets data from content provider and create json string.

Suggested solution has same result !

ClassA obj = new ClassA(ID,Records); 
Gson gson = new GsonBuilder().serializeNulls().create();
Toast.makeText(getActivity(), gson.toJson(obj), Toast.LENGTH_LONG).show();

Toast shows {} again.

Montri M :

Just my wild guess, I saw you mentioned in one of the comment that ClassA and ClassB are inner classes inside a fragment. So, maybe there's a few thing you could try, if haven't already.

First: How about changing them into static inner class?

private static class ClassA {
    ...
}

private static class ClassB {
    ...
}

Having classes declared as inner class because no one else use it is okay, I did that sometimes too. But non-static inner class kind of depended on its parent object instance, so declaring them static inner class is much safer, when it's just a simple bean/DTO classes.

Second: Try fiddling around Proguard configuration about those classes

  • Here's a stackoverflow question about setting up Proguard to keep inner classes .

  • Review the -keep class <your packge name>.** { <fields>; } part

Third: Maybe try extracting those classes into a normal class just to narrow down the suspect area.

If it still doesn't work, then the problem probably lies somewhere else, maybe some specific build/version of the client mobile device or something.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=322786&siteId=1