jacksonproguard

Jackson + ProGuard: can't deserialize class that uses custom deserializer


My app uses Jackson. I got minimized builds mostly working with this configuration:

# don't obfuscate Jackson classes
-keep class com.fasterxml.** { *; }

# don't strip runtime annotations
-keepattributes RuntimeVisibleAnnotations

# keep members with Jackson annotations
-keepclassmembers public class * {
     @com.fasterxml.jackson.annotation.JsonCreator *;
     @com.fasterxml.jackson.annotation.JsonProperty *;
}

But minimized builds are still unable to deserialize one class that uses a custom deserializer, which in turn uses a factory. The factory, deserializer, and mapper are all produced by Dagger2. The error is:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.mycompany.myapp.c.y]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
            at [Source: N/A; line: -1, column: -1]
            at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(Unknown Source)
            at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(Unknown Source)
            at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(Unknown Source)
            at com.fasterxml.jackson.databind.ObjectMapper._readValue(Unknown Source)
            at com.fasterxml.jackson.databind.ObjectMapper.readValue(Unknown Source)
            at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(Unknown Source)
            at com.mycompany.myapp.c.x.a(Unknown Source)
            at com.mycompany.myapp.c.x.a(Unknown Source)
            at com.mycompany.myapp.c.x.deserialize(Unknown Source)
            at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(Unknown Source)
            at com.fasterxml.jackson.databind.ObjectMapper.readValue(Unknown Source)
            at com.mycompany.myapp.c.ag.d(Unknown Source)
            at com.mycompany.myapp.c.ag.a(Unknown Source)
            at com.mycompany.myapp.c.at.run(Unknown Source)
            at android.os.Handler.handleCallback(Handler.java:605)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4508)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:809)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:576)
            at dalvik.system.NativeStart.main(Native Method)

The error message suggests that my class lacks a default constructor, which indeed it does because instances are created by the custom deserializer using the factory. This works fine when I disable ProGuard. What do I need to do to make it work with ProGuard?


Solution

  • I accidentally discovered this handy troubleshooting technique:

    -keepnames class * { *; }
    

    This revealed the true culprit to be a private static nested class inside my custom deserializer. The solution was to remove public from the class specifier on this option:

    -keepclassmembers class * {
         @com.fasterxml.jackson.annotation.JsonCreator *;
         @com.fasterxml.jackson.annotation.JsonProperty *;
    }