What if I use GSON serialization

GSON uses reflection to determine the names of the attributes in the serialized JSON representation of your objects. If you obfuscate the names of class attributes, you will end up with JSON that contains obfuscated names as well. So instead of having`{ "name": "John", "last_name": "Smith" }` the serialized object will look like `{ "a0": "John", "a1": "Smith" }`.

What further complicates the matter is that by default ProGuard and R8 will generate a different name for every class and attribute with every build. This means that the next versions of your app will not be able to read objects serialized by the previous versions.

Usually both of the effects above are not desirable. This means that in your code you will want to keep serialized class members from being obfuscated. It is a good idea to have a common factor for your serialized classes, which you can use for targeting - for example an annotation, like this:

    -keep @com.mycompany.dataclass public class *

In many cases you are going to be using Expose and/or SerializedName annotations in your classes to limit serialized attributes and define their names. If you do, you can use the following rule set:

    -dontnote com.google.gson.annotations.Expose
    -keepclassmembers class * {
        @com.google.gson.annotations.Expose <fields>;
    }


    -keepclasseswithmembers,allowobfuscation,includedescriptorclasses class * {
        @com.google.gson.annotations.Expose <fields>;
    }

    -dontnote com.google.gson.annotations.SerializedName
    -keepclasseswithmembers,allowobfuscation,includedescriptorclasses class * {
        @com.google.gson.annotations.SerializedName <fields>;
    }

One particular case is enum values. To handle them correctly add SerializedName annotation to the enum values and use the rule like this:

In your source code:

        public enum Animal {
       @SerializedName("kangaroo")
       Kangaroo,
       @SerializedName("lion")
       Lion
    }

In your ProGuard configuration:

    -keepclassmembers enum * {
        @com.google.gson.annotations.SerializedName <fields>;
    }
1 Like

This seems different from what I’ve found while using ProGuard and R8 with GSON. The rules I’ve used are:

 # GSON
 -dontnote com.google.gson.**
 # GSON TypeAdapters are only referenced in annotations so ProGuard doesn't find their method usage
 -keepclassmembers,allowobfuscation,includedescriptorclasses class * extends com.google.gson.TypeAdapter {
     public <methods>;
 }
 # GSON TypeAdapterFactory is an interface, we need to keep the entire class, not just its members
 -keep,allowobfuscation,includedescriptorclasses class * implements com.google.gson.TypeAdapterFactory
 # GSON JsonDeserializer and JsonSerializer are interfaces, we need to keep the entire class, not just its members
 -keep,allowobfuscation,includedescriptorclasses class * implements com.google.gson.JsonDeserializer
 -keep,allowobfuscation,includedescriptorclasses class * implements com.google.gson.JsonSerializer
 # Ensure that all fields annotated with SerializedName will be kept
 -keepclassmembers,allowobfuscation class * {
     @com.google.gson.annotations.SerializedName <fields>;
 }

Then all class members that are relevant for GSON need to be annotated with @SerializedName, and their actual variable names will still be obfuscated.
Classes that are not used gets removed, which is OK. This also means that I don’t need any keep-rule to artificially keep every class that happens to have any GSON annotations, in case I’ve written model-classes that are not in use yet, or if the models are a part of a bigger library where I only use a smaller part of it.

The rules about JsonDeserializer, JsonSerializer and TypeAdapter are necessary when using annotations like @JsonAdapter(MyAdapter::class), since those classes might not be referenced anywhere other than in the annotation.

Hi @Thorbear, the approach with @SerializedName works and makes sense as well. You will have to annotate every member, but the upside is indeed as you indicated that you will not need any “artificial” rules, and therefore you can remove the classes you don’t use during optimization (unless you use them through reflection of course).

1 Like