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.

A Farmanbar
  • 4,381
  • 5
  • 24
  • 42
  • try adding `getters` to both of the classes – Ryuzaki L Nov 05 '19 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. – Karol Dowbecki Nov 05 '19 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 Nov 05 '19 at 22:29
  • @KarolDowbecki it's not multi-threading – A Farmanbar Nov 05 '19 at 22:33
  • @KarolDowbecki reports only shows {} nothing more – A Farmanbar Nov 05 '19 at 22:34
  • Please provide a [mcve]. Gson doesn't serialize nulls be default. – Sotirios Delimanolis Nov 05 '19 at 22:34
  • @Jameson i put more – A Farmanbar Nov 05 '19 at 22:51
  • are you set proguard rules for enteties? – Abolhassan Pirayeh Nov 05 '19 at 23:02
  • @AbolhassanPirayeh yea – A Farmanbar Nov 05 '19 at 23:03
  • @SotiriosDelimanolis see edit – A Farmanbar Nov 05 '19 at 23:05
  • Do you use free hosting? Because I had encountered this problem in free hosts before. The problem was that an invisible character would stick to the end of the output string and Jason would become invalid, this would not always happen but sometimes it would. – Abolhassan Pirayeh Nov 05 '19 at 23:22
  • @AbolhassanPirayeh the problem happens in phone before sending to server or host. – A Farmanbar Nov 05 '19 at 23:27
  • 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 Nov 05 '19 at 23:58
  • @MCEmperor are you sure it's the problem? because i have to publish and see feedback in production mode .all works correct for me but i am not sure how does it work for users.are you sure? – A Farmanbar Nov 06 '19 at 00:00
  • The naming convention won't affect whether it works or not, but I do recommend following standard Java naming conventions. – retodaredevil Nov 06 '19 at 02:20
  • @MrAF That won't fix the issue, otherwise I would have posted it as answer. However, it'll make your code better readable for yourself and others, so I strongly recommend doing so. – MC Emperor Nov 06 '19 at 06:25
  • @MCEmperor i am appreciate you suggestion. i will do . – A Farmanbar Nov 06 '19 at 08:05
  • I couldn't help notice, both are private class with no default constructor!! Try adding default constructor and make the classes public. Gson uses reflection for acccessing the values and the name of the fields. In such cases, the access moodifiers play an important role. – JavaGhost Nov 11 '19 at 18:41
  • For which devices you're getting this problem – Rahul Devanavar Nov 13 '19 at 04:44
  • So, I'd like to remove my answer down there, because it does not help you. I'd put it as a comment right here. But to solve your problem: please provide us your app (a simplest app which has the problem you've got) and provide a emulator in which we can reproduce the problem. Thanks. – Janos Vinceller Apr 02 '20 at 14:04
  • In your code, there are some interesting things to point out: Normally, I would always use List (one of the interfaces of ArrayList) as a type to store a list in it. ArrayList would be the specific type of the object passed there in.Variable names always small with camel case. For your problem: - Gson does not convert null members by default. - Gson does not convert transient members by default. Use GsonBuilder to customize a Gson instance for your case, for your need. – Janos Vinceller Apr 02 '20 at 14:05

5 Answers5

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.

retodaredevil
  • 1,261
  • 1
  • 13
  • 20
  • @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. – retodaredevil Nov 05 '19 at 23:01
  • see last section of question – A Farmanbar Nov 05 '19 at 23:03
  • @Mr.AF Ah I didn't see that. Can you tell us what values of ID and Records make it give "{}"? – retodaredevil Nov 05 '19 at 23:08
  • see database records in question – A Farmanbar Nov 05 '19 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. – retodaredevil Nov 05 '19 at 23:14
  • In my opinion this is closest to truth. – ror Nov 14 '19 at 18:41
4

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.

Montri M
  • 1,716
  • 14
  • 12
  • 1
    Sounds good.your suggestions sounds practical and somehow might resolve problem . testing it takes times.i will test and feedback . – A Farmanbar Nov 14 '19 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
}
  • 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 – A Farmanbar Nov 06 '19 at 08: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" 
}
Isai Damier
  • 976
  • 6
  • 8
-1

Try this

GsonBuilder builder = new GsonBuilder().serializeSpecialFloatingPointValues();
 String data = builder.create().toJson(obj);
Kintan Patel
  • 1,230
  • 8
  • 15