14
votes

I'm using Jackson to deserialize Json responses from Retrofit.

I use the Jackson Module Kotlin library for that.

I have data classes with default values for some values that Java represents as primitives, so it won't crash when accessing it without null check.

This all works fine in debug mode, but when I run on release with proguard enabled, default values are not set and those values are null, causing my app to crash when accessing them from Java assuming primitives.

I've tried to add every proguard rule I could find online, but with no success.

If anyone has any idea, please share.

Thanks

Example of a data class

data class RideTask(@JsonProperty(RiderFrontendConsts.PARAM_LOCATION)
                    val location: UserVisibleLocation?,

                    @JsonProperty(RiderFrontendConsts.PARAM_ETA_TS)
                    val etaTime: Double?,

                    @JsonProperty(RiderFrontendConsts.PARAM_TIME_TO_COMPLETION)
                    val timeToCompletion: Double?,

                    @JsonProperty(RiderFrontendConsts.PARAM_ETA_DESCRIPTION)
                    val etaDescription: String?,

                    @JsonInclude(JsonInclude.Include.NON_NULL)
                    @JsonProperty(RiderFrontendConsts.PARAM_INTERNAL_ETA_TS)
                    val internalEta: Long? = 0,

                    @JsonProperty(RiderFrontendConsts.PARAM_ETA_DESCRIPTION_LIST)
                    val etaDescriptionList: List<String>?,

                    @JsonProperty(RiderFrontendConsts.PARAM_DESCRIPTION_PREFIX)
                    val descriptionPrefix: String?,

                    @JsonProperty(RiderFrontendConsts.PARAM_WALKING_DISTANCE_DESCRIPTION)
                    val walkingDistanceDescription: String?,

                    @JsonProperty(RiderFrontendConsts.PARAM_WALKING_DISTANCE_IN_METERS)
                    val walkingDistanceInMeters: Int? = 0)
    : Serializable

Retrofit and Jackson initialisation

private Retrofit getRetrofit() {
    LOGGER.debug("prepare retrofit");
    return new Retrofit.Builder()
            .client(getHttpClient(RiderFrontendConsts.DEFAULT_TIMEOUT_IN_MILLIS))
            .baseUrl(SettingsManager.getInstance(Application.getInstance()).getServerBaseUrl())
            .addConverterFactory(JacksonConverterFactory.create(getObjectMapper()))
            .callbackExecutor(Executors.newCachedThreadPool())
            .build();
}

private static ObjectMapper getObjectMapper() {
    LOGGER.debug("prepare object mapper");
    return ExtensionsKt.jacksonObjectMapper()
            .enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
            .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
            .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
            .enable(SerializationFeature.WRITE_ENUMS_USING_INDEX)
            .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
            .setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.ANY)
            .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
            .setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
            .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);
}

Related pro guard rules

#parsers
-keep class com.fasterxml.jackson.annotation.** { *; }
-dontwarn com.fasterxml.jackson.databind.**

# Kotlin
-keep class kotlin.Metadata { *; }
-keepclassmembers public class via.rider.frontend.** {
    public synthetic <methods>;
}
2
I'm from a backend realm, I don't have any kind of proguard there, but in order to make Jackson work with Kotlin stuff I have to add jackson-module-kotlin to the classpath and call val mapper = ObjectMapper().registerKotlinModule() extension function from there. Hope this helps.Oleg
Thank you @oleg, I'm already using it. It's definitely something with pro guard, since it works just fine without itorelzion

2 Answers

0
votes

You need to keep your data class files (package for RideTask - my example assumes a dto package that RideTask lives in), something like this:

-keep,includedescriptorclasses class via.rider.dto.** { *; }

If this doesn't solve the issue for you, please post a logcat with the parsing errors.

0
votes

Think the problem is that you have to add a rule in order to keeping the field name, something like

-keep class your.package.model.** { <fields>; }