[英]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
中的ID
和Records
是 null。 Gson 默认情况下不序列化空值,实际上有一种方法可以启用序列化空值,我几乎总是启用它:
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
我建议保留一个 Gson 的 static 实例,这样您就不必在每次想要序列化时都创建一个。
如果您不想序列化空值,那么您还有其他一些选项来修复您的代码,例如在ClassA
的构造函数中检查 null 。
只是我的猜测,我看到您在其中一条评论中提到ClassA
和ClassB
是片段内的内部类。 所以,也许你可以尝试一些事情,如果还没有的话。
第一:如何将它们更改为 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
}
在您的代码中,有一些有趣的事情需要指出:
对于您的问题: - 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.