繁体   English   中英

Gson.toJson 返回 null [ProGuard 问题]

[英]Gson.toJson returns null [ProGuard issue]

用户错误报告显示Gson().toJson(obj)偶尔返回{}但对于大多数用户来说它工作正常。

我拜访了一位用户,他在手机上遇到了错误并调试了应用程序,我制作了Toast以显示发送到服务器的内容,我看到 Toast 显示{}并且记录ID不是null

这就是我所做的。

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;
        }

}

在这里我序列化 object

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

但是对于某些用户的new Gson().toJson(obj ) 工作正常,但对于某些返回{}

服务器数据库显示一些用户发送数据{} ,但一些正确的 json。经过一些研究,我发现new Gson().toJson(obj ) 返回{} 。没有网络服务问题,也没有数据库问题。

额外信息

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);

数据库

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

c90   | {} bug

Null 输入测试

我做了下面的测试,我发现问题超出了 null 输入

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

Toast 显示{"Records":[]} 。比上限条件更糟糕的情况永远不会发生

还有 IDE 说

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  ----------

我如何使它对所有用户都正确工作?

注释

这是不可能的,您需要有关 {} 案例的更多详细信息,例如它是否一致、是否由于多线程、是否是特定的 JDK 版本

没有任何多线程现象。例如,没有Runnable没有AsycTask没有Thread 。它只是一个从内容提供者获取数据并创建 json 字符串的普通片段。

建议的解决方案具有相同的结果!

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

Toast 再次显示{}

这是因为ClassA中的IDRecords是 null。 Gson 默认情况下不序列化空值,实际上有一种方法可以启用序列化空值,我几乎总是启用它:

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

我建议保留一个 Gson 的 static 实例,这样您就不必在每次想要序列化时都创建一个。

如果您不想序列化空值,那么您还有其他一些选项来修复您的代码,例如在ClassA的构造函数中检查 null 。

只是我的猜测,我看到您在其中一条评论中提到ClassAClassB是片段内的内部类。 所以,也许你可以尝试一些事情,如果还没有的话。

第一:如何将它们更改为 static 内部 class?

private static class ClassA {
    ...
}

private static class ClassB {
    ...
}

将类声明为内部 class 因为没有其他人使用它是可以的,我有时也这样做。 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.

第二:尝试摆弄有关这些类的Proguard配置

  • 这是关于设置Proguard以保留内部类的 stackoverflow 问题

  • 查看-keep class <your packge name>.** { <fields>; } -keep class <your packge name>.** { <fields>; }部分

第三:也许尝试将这些类提取到普通的 class 中,以缩小可疑区域。

如果它仍然不起作用,那么问题可能出在其他地方,可能是客户端移动设备的某个特定构建/版本或其他东西。

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

在这种情况下,您 rRecord arraylist 是空的,而不是 null。 试试这个检查

if(!Records.isEmpty()){ 
 // show toast
}

在您的代码中,有一些有趣的事情需要指出:

  • 为什么要开设私教课? 那里需要吗?
  • 通常,我总是使用 List(ArrayList 的接口之一)作为在其中存储列表的类型。 ArrayList 将是传入的 object 的特定类型。
  • 变量名总是很小的驼峰式大小写。

对于您的问题: - Gson 默认不转换 null 成员。 - Gson 默认不转换临时成员。

使用 GsonBuilder 为您的案例定制一个 Gson 实例,满足您的需要。

感谢@Montri M 的好建议:

第二:尝试摆弄有关这些类的 Proguard 配置

这是关于设置 Proguard 以保留内部类的 stackoverflow 问题。

查看 -keep class.** {; } 部分

第三:也许尝试将这些类提取到普通的 class 中,以缩小可疑区域。

我创建了DataModel并提取了内部类并将它们插入DataModel

public class DataModel {

    public static class ClassA{
        String ID;
        ArrayList<ClassB> Records;

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

    public static class ClassB {
        int T;
        int F;
        String D;

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

    }
}

我将这两行放入Proguard-rules.pro 。请注意$符号

-keep class <package-name>.DataModel.** { *; }
-keep class <package-name>.DataModel$** { *; }

现在我看到问题已解决并且 Gson 序列化 obj 正确。

您是否可能在某处捕获JsonParseException 当未向TypeAdapter注册类型时, RuntimeTypeAdapterFactory有时会导致麻烦。 尝试征用RuntimeTypeAdapterFactory class 并在您的项目使用的版本中,替换每次出现

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

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" 
}

尝试这个

GsonBuilder builder = new GsonBuilder().serializeSpecialFloatingPointValues();
 String data = builder.create().toJson(obj);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM