Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson.toJson returns null [ProGuard issue]

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.

like image 349
A Farmanbar Avatar asked Nov 05 '19 22:11

A Farmanbar


People also ask

Does GSON serialize nulls?

By default, the Gson object does not serialize the fields with null values to JSON. If a field in a Java object is null, Gson excludes it. We can force Gson to serialize null values via the GsonBuilder class.

Is GSON toJson thread safe?

Gson is typically used by first constructing a Gson instance and then invoking toJson(Object) or fromJson(String, Class) methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple threads.

What does GSON toJson do?

Gson is the main actor class of Google Gson library. It provides functionalities to convert Java objects to matching JSON constructs and vice versa. Gson is first constructed using GsonBuilder and then toJson(Object) or fromJson(String, Class) methods are used to read/write JSON constructs.


1 Answers

This is because ID and Records in ClassA are null. Gson does not serialize nulls by default and actually has a method to enable serializing nulls, which I almost always enable:

private static final Gson GSON = new GsonBuilder().serializeNulls().create();

I recommend keeping a static instance of a Gson around so you don't have to keep creating one each time you want to serialize.

If you don't want to serialize nulls, then you have some other options to fix your code, such as having null checks in the constructor of ClassA.

like image 145
retodaredevil Avatar answered Oct 10 '22 19:10

retodaredevil