AssertionError in Gson EnumTypeAdapter when using Proguard Obfuscation

mon projet implémente un TypeAdapter dans Gson pendant la sérialisation/désérialisation pour préserver l'état de polymorphisme de l'objet. Quoi qu'il en soit, le projet fonctionne très bien pendant les tests de développement, mais quand il est publié avec proguard obfuscation et testé, il se plante.

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.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.<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.serialize(SourceFile:137)

ma configuration de proguard spécifique Gson est:

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

Le TypeAdapter que j'utilise est:

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

j'ai fait beaucoup de recherche sur cette erreur spécifique à Gson, mais je n'ai pas pu trouver de réponse utile. Cependant, j'ai trouvé une autre question avec la question similaire.

toute aide de la communauté du développeur serait appréciée.

39
demandé sur Community 2013-03-21 13:26:00

5 réponses

il semble que nous devions demander que les membres des énumérations soient gardés. Cela a fonctionné pour moi:

-keepclassmembers enum * { *; }

Ou, si vous voulez être plus précis,

-keepclassmembers enum com.your.package.** { *; }
102
répondu pandre 2015-05-11 11:55:42

Gson lance cette AssertionError quand il ne parvient pas à desérialiser les constantes d'énumération à partir des données JSON, en effectuant une introspection sur les champs de la classe enum. Malheureusement, il avale les détails de la NoSuchFieldException sous-jacente.

vous devez vous assurer que vous préservez les noms des champs enum (et des champs en général) qui sont sérialisés. Par défaut, ProGuard peut les renommer ou même les supprimer. Par exemple, avec quelques caractères génériques:

-keepclassmembers class com.example.domain.** {
    <fields>;
}
17
répondu Eric Lafortune 2014-05-04 01:05:10

il est déjà suggéré que vous devez configurer Proguard d'une manière qui garde intact tout enum lié aux objets sérialisés. Je n'aime pas vraiment le fait que je dois énumérer explicitement tous mes énums, cette solution est difficile à maintenir. Une solution légèrement meilleure que j'ai trouvée est la suivante.

utiliser une interface vide pour indiquer qu'une classe ou un enum participe à la sérialisation Gson:

public interface GsonSerializable { }

public class MyClass implements GsonSerializable {

    public enum MyEnum implements GsonSerializable {
        enumvalue1, enumvalue2
    }

    public MyEnum mydata1;
}

utiliser une configuration Proguard qui conserve à la fois l'interface et toutes les classes/énums qui l'implémentent:

# 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

C'est ça, tant que vos classes et enums utilisent l'interface, ça devrait aller. Vous pouvez également renforcer la présence de cette interface dans vos méthodes de sérialisation/deserialisation, de sorte que vous ne l'oubliez pas lors de l'ajout d'une nouvelle classe plus tard:

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

aussi dans votre configuration la ligne avec 'com.Google.gson.exemple.Android.modèle.** { * ;} "se rapporte à certains Exemple de code lié à Google, donc je ne pense pas que ce soit nécessaire.

11
répondu Levente Dobson 2014-05-16 13:17:48

après avoir rencontré le même problème, j'ai examiné L'APK décompilé. Je pense que le problème est lié au fait qu'un type d'enum perd ses membres pendant l'obscurcissement.

assurez-vous de garder des énums:

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

aussi - s'assurer que toutes les classes utilisées dans GSON sont retenues:

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

Voir tous les config @ pastebin.com/r5Jg3yY2

5
répondu Eggman87 2013-03-21 15:01:05

dans mon cas, proguard a été configuré pour -keep classes individuelles touchées par Gson, mais l'erreur a disparu quand j'ai configuré proguard pour conserver le paquet où ces classes individuelles résidaient:

-keep class com.company.library.model.** { *; }
5
répondu Rich Ehmer 2015-02-23 18:09:48