6

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.

21
  • try adding getters to both of the classes
    – Ryuzaki L
    Commented Nov 5, 2019 at 22:14
  • 2
    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. Commented Nov 5, 2019 at 22:20
  • 1
    This looks like something you should post as a bug report to the Gson project. But, they'll ask you to provide more information than you have here. Heads up.
    – Jameson
    Commented Nov 5, 2019 at 22:29
  • @KarolDowbecki it's not multi-threading Commented Nov 5, 2019 at 22:33
  • 1
    You should follow the Java Naming Conventions: variabele names should be written in camelCase, e.g. Records should be records, ID should be id and T, F and D should be t, f and d respectively.
    – MC Emperor
    Commented Nov 5, 2019 at 23:58

5 Answers 5

4

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.

6
  • @Mr.AF you said you are viewing the JSON data in a server database. Can you confirm that any JSON data is "{}" by print statements or debug logging? It's possible that the error isn't in the code you posted and is in the way the json is uploaded or viewed. Commented Nov 5, 2019 at 23:01
  • see last section of question Commented Nov 5, 2019 at 23:03
  • @Mr.AF Ah I didn't see that. Can you tell us what values of ID and Records make it give "{}"? Commented Nov 5, 2019 at 23:08
  • see database records in question Commented Nov 5, 2019 at 23:09
  • 1
    @Mr.AF Yes, I see that the database contains a "{}". Can you locally debug and see what the values of ID and Records are when the json string equals "{}". Maybe debug all three values in a Toast, print statement, or log statement. Commented Nov 5, 2019 at 23:14
4
+50

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.

1
  • 1
    Sounds good.your suggestions sounds practical and somehow might resolve problem . testing it takes times.i will test and feedback . Commented Nov 14, 2019 at 10:06
0
ArrayList<ClassB> Records= new ArrayList<>();
ClassA obj = new ClassA(null,Records);    
Toast.makeText(getApplicationContext(),new Gson().toJson(obj 
),Toast.LENGTH_LONG).show();

In this case you rRecord arraylist is empty not null. try this checking

if(!Records.isEmpty()){ 
 // show toast
}
1
  • yea it's empty .as i said this condition is worse condition which might happen but as i said IDE says the worse condition never happens although i assumed to happens Commented Nov 6, 2019 at 8:07
0

Are you possibly catching a JsonParseException somewhere? RuntimeTypeAdapterFactory sometimes causes trouble when a type is not registered with the TypeAdapter. Try commandeering the RuntimeTypeAdapterFactory class and in the version that your project uses, replace every occurrence of

if (delegate == null) {
          throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
              + label + "; did you forget to register a subtype?");
        }

With

if (delegate == null) {
    /*
    This class is commandeered from Google/Gson. The edit below was added so that if a type
    is not registered with the TypeAdapter, the app still works instead of throwing an
    exception and crash.
    */
    delegate = (TypeAdapter<R>) labelToDelegate.get(Constants.DEFAULT_TYPE); // "DefaultType" 
}
1
  • The hardness of resolving this problem was that there was not any exception ... Commented Nov 14, 2019 at 21:01
-1

Try this

GsonBuilder builder = new GsonBuilder().serializeSpecialFloatingPointValues();
 String data = builder.create().toJson(obj);
3
  • how does it help?what differences ?why? Commented Nov 8, 2019 at 18:52
  • It will help when your JSON has floating value and it try to covert in String Commented Nov 9, 2019 at 6:22
  • thanks.but all values are int and there isn't any float value.values are restored from content provider which its data type is INT. Commented Nov 9, 2019 at 7:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.