Gson EnumTypeAdapter 使用前卫模糊处理时的断言错误

我的项目在序列化/反序列化期间在 Gson中实现 TypeAdapter,以保持对象的多态性状态。无论如何,该项目在开发测试期间工作良好,但是当它与 前卫的混淆一起发布并进行测试时,它就会崩溃。

03-21 10:06:53.632: E/AndroidRuntime(12441): FATAL EXCEPTION: main
03-21 10:06:53.632: E/AndroidRuntime(12441): java.lang.AssertionError
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.TypeAdapters$EnumTypeAdapter.<init>(SourceFile:724)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.TypeAdapters$26.create(SourceFile:753)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.getAdapter(SourceFile:353)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(SourceFile:82)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(SourceFile:81)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(SourceFile:118)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(SourceFile:72)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.getAdapter(SourceFile:353)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.toJson(SourceFile:578)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.toJsonTree(SourceFile:479)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.toJsonTree(SourceFile:458)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson$3.serialize(SourceFile:137)

我的 Gson 特定的前卫配置是:

##---------------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
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }


# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }


#This is extra - added by me to exclude gson obfuscation
-keep class com.google.gson.** { *; }


##---------------End: proguard configuration for Gson  ----------

我使用的 字体适配器是:

public final class GsonWorkshiftAdapter implements JsonSerializer<IWorkshift>, JsonDeserializer<IWorkshift> {
private static final String CLASSNAME = "CLASSNAME";
private static final String INSTANCE  = "INSTANCE";


@Override
public JsonElement serialize(IWorkshift src, Type typeOfSrc, JsonSerializationContext context) {
String className = src.getClass().getCanonicalName();
JsonElement elem = context.serialize(src);


JsonObject retValue = new JsonObject();
retValue.addProperty(CLASSNAME, className);
retValue.add(INSTANCE, elem);


return retValue;
}


@Override
public IWorkshift deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject =  json.getAsJsonObject();
JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
String className = prim.getAsString();


Class<?> klass = null;
try { klass = Class.forName(className); }
catch (ClassNotFoundException e) { throw new JsonParseException(e.getMessage()); }


return context.deserialize(jsonObject.get(INSTANCE), klass);
}
}

我对这个特定于 Gson 的错误进行了大量的搜索,但是没有找到任何有用的答案。然而,我发现 另一个问题也有类似的问题。

任何来自开发者社区的帮助都将不胜感激。

21355 次浏览

在遇到同样的问题之后,我查看并检查了由此产生的 APK 反编译。我认为这个问题与某些枚举类型在混淆期间失去其成员有关。

一定要保留枚举:

 -keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}

另外,确保 GSON 中使用的所有类都被保留:

 -keep public class com.company.ordering.datacontract.** {
public protected *;
}


-keep public class com.company.ordering.service.request.** {
public protected *;
}
-keep public class com.company.ordering.service.response.** {
public protected *;
}

见完整的 config@ pastebin.com/r5jg3yy2

当 GSON 无法从 JSON 数据反序列化枚举常数时,它会抛出这个 AssertionError,对枚举类的字段执行自省。不幸的是,它吞噬了底层 NoSuchFieldException 的细节。

应确保保留序列化的枚举字段(以及一般字段)的名称。默认情况下,ProGuard 可以重命名甚至删除它们。例如,使用一些通配符:

-keepclassmembers class com.example.domain.** {
<fields>;
}

已经建议您需要配置 ProGuard,使其保持与序列化对象相关的每个枚举的完整性。我真的不喜欢我必须显式列出所有枚举的事实,这个解决方案很难维护。我想到的一个稍微好一点的解决方案是这样的。

使用一个空接口表示一个类或枚举参与 Gson 序列化:

public interface GsonSerializable { }


public class MyClass implements GsonSerializable {


public enum MyEnum implements GsonSerializable {
enumvalue1, enumvalue2
}


public MyEnum mydata1;
}

使用 ProGuard 配置来保持接口和所有实现它的类/枚举:

# keep GsonSerializable interface, it would be thrown away by proguard since it is empty
-keep class com.example.GsonSerializable


# member fields of serialized classes, including enums that implement this interface
-keepclassmembers class * implements com.example.GsonSerializable {
<fields>;
}


# also keep names of these classes. not required, but just in case.
-keepnames class * implements com.example.GsonSerializable

就是这样,只要您的类和枚举使用接口,就应该没问题。您还可以在序列化/反序列化方法中强制这个接口的存在,这样以后添加新类时就不会忘记:

public String serializeWithGson(GsonSerializable object) { ... }

在您的配置中,还有‘ com.google.gson.examples.android.model 行。“ * * * ; }”引用了一些与 Google 相关的示例代码,所以我认为没有必要。

在我的例子中,proGuard 被配置为 Gson 接触到的 -keep个别类,但是当我配置 proGuard 以保留这些个别类所在的 包裹时,错误消失了:

-keep class com.company.library.model.** { *; }

似乎我们必须要求保留枚举的成员。 把它添加到前卫配置文件对我来说很有用:

-keepclassmembers enum * { *; }

或者,如果你想说得更具体一点,

-keepclassmembers enum com.your.package.** { *; }

请核实以下事项-

  1. 添加应用程序目录中的 proguard-rules.pro 文件。

  2. 在 build.gradle (module: app)文件路径定义中定义的路径是正确的,如下所示

    ProguardFiles getDefaultProguardFile (‘ proGuard-android-Optimize.txt’) ,‘ proguard-rules.pro’

  3. 如果以上两个步骤是可以的,那么请在提案文件中添加以下一行(规则)-

    - keep classmember enum * { * ; }

  4. 清理、生成项目并再次运行。

在枚举类上应用 Androidx 注释,保留注释,如:

@Keep
enum class PlayerType {
PRO,
INTERMEDIATE,
BASIC
}