Danish Ansari
Danish Ansari

Reputation: 447

Jackson serialisation fails by throwing KotlinReflectionInternalError when serializing model with empty list and minifyEnabled true

I have minifyEnabled true in my build.gradle file and let's say I have a model

data class SomeModel(
    val numberList: List<Int>
)

It get's serialized properly when numberList variable have some value's

SomeModel(listOf(1, 2, 3))

but the serialization fails if the content of numberList is empty

SomeModel(listOf()) // or SomeModel(emptyList())

Above both cases work fine if I change code to minifyEnabled false

I serialize like this

jsonMapper.writeValueAsString(someModel)

My Jackson's JsonMapper looks like this

JsonMapper().apply {
    enable(MapperFeature.INFER_PROPERTY_MUTATORS)
    enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
    configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    registerModule(KotlinModule())
}

I even have added the class in proguard-rules.pro file

-keep  class org.package.SomeModel {
    <fields>;
}

# tried this also
-keep  class org.package.SomeModel {
    *;
}

The error log are as follows

kotlin.reflect.jvm.internal.KotlinReflectionInternalError: No accessors or field is found for property val kotlin.collections.EmptyList.serialVersionUID: kotlin.Long
    at kotlin.reflect.jvm.internal.KPropertyImplKt.computeCallerForAccessor(KPropertyImpl.kt:240)
    at kotlin.reflect.jvm.internal.KPropertyImplKt.access$computeCallerForAccessor(KPropertyImpl.kt:1)
    at kotlin.reflect.jvm.internal.KPropertyImpl$Getter$caller$2.invoke(KPropertyImpl.kt:156)
    at kotlin.reflect.jvm.internal.KPropertyImpl$Getter$caller$2.invoke(KPropertyImpl.kt:147)
    at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:62)
    at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
    at kotlin.reflect.jvm.internal.KPropertyImpl$Getter.getCaller(Unknown Source:7)
    at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63)
    at com.fasterxml.jackson.module.kotlin.KotlinAnnotationIntrospector.getCorrespondingGetter(KotlinAnnotationIntrospector.kt:145)
    at com.fasterxml.jackson.module.kotlin.KotlinAnnotationIntrospector.hasRequiredMarker(KotlinAnnotationIntrospector.kt:110)
    at com.fasterxml.jackson.module.kotlin.KotlinAnnotationIntrospector.access$hasRequiredMarker(KotlinAnnotationIntrospector.kt:23)
    at com.fasterxml.jackson.module.kotlin.KotlinAnnotationIntrospector$hasRequiredMarker$hasRequired$1.invoke(KotlinAnnotationIntrospector.kt:40)
    at com.fasterxml.jackson.module.kotlin.KotlinAnnotationIntrospector$hasRequiredMarker$hasRequired$1.invoke(KotlinAnnotationIntrospector.kt:23)
    at com.fasterxml.jackson.module.kotlin.ReflectionCache.javaMemberIsRequired(ReflectionCache.kt:56)
    at com.fasterxml.jackson.module.kotlin.KotlinAnnotationIntrospector.hasRequiredMarker(KotlinAnnotationIntrospector.kt:33)
    at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.hasRequiredMarker(AnnotationIntrospectorPair.java:318)
    at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.hasRequiredMarker(AnnotationIntrospectorPair.java:318)
    at com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder.getMetadata(POJOPropertyBuilder.java:229)
    at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._anyIndexed(POJOPropertiesCollector.java:1197)
    at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._sortProperties(POJOPropertiesCollector.java:1099)
    at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:425)
    at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getJsonValueAccessor(POJOPropertiesCollector.java:227)
    at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findJsonValueAccessor(BasicBeanDescription.java:258)
    at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.findSerializerByAnnotations(BasicSerializerFactory.java:391)
    at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.buildCollectionSerializer(BasicSerializerFactory.java:705)
    at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.buildContainerSerializer(BasicSerializerFactory.java:647)
    at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:200)
    at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:169)
    at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1473)
    at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1441)
    at com.fasterxml.jackson.databind.SerializerProvider.findPrimaryPropertySerializer(SerializerProvider.java:651)
    at com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap.findAndAddPrimarySerializer(PropertySerializerMap.java:72)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._findAndAddDynamic(BeanPropertyWriter.java:895)
E/AndroidRuntime:     at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:706)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
    at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4485)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3740)
    at org.nvest.BiTool.test1(BiTool.kt:173)
    at org.bharti.axa.ui.HomeScreenActivity$onCreate$4.onClick(HomeScreenActivity.kt:92)
    at android.view.View.performClick(View.java:7509)
    at android.view.View.performClickInternal(View.java:7486)
    at android.view.View.access$3600(View.java:841)
    at android.view.View$PerformClick.run(View.java:28709)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:236)
    at android.app.ActivityThread.main(ActivityThread.java:8060)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

Upvotes: 2

Views: 768

Answers (1)

Justin T
Justin T

Reputation: 94

The error indicates that No accessors or field is found for property val kotlin.collections.EmptyList.serialVersionUID: kotlin.Long

So kotlin.reflect knows about EmptyList.serialVersionUID but it can't find it for Jackson to serialize the empty list. For it to know about EmptyList.serialVersionUID while it's not actually accessible likely means that it was in the kotlin.Metadata of kotlin.collections.EmptyList but not in the class itself.

Since this happens only with minifyEnabled = true, it appears that r8 stripped kotlin.collections.EmptyList.serialVersionUID but didn't actually update the kotlin.Metadata of kotlin.collections.EmptyList to avoid incorrectly indicating that the field is still there.

This is likely a bug with r8, and r8 is packaged with the android gradle plugin, so updating the android gradle plugin to the latest stable release should resolve the issue. If it doesn't, I recommend filing an r8 bug in the Google Issue Tracker.

Upvotes: 3

Related Questions