Description
Describe the bug
A data class that has an inline class-typed property can't be deserialized.
To Reproduce
Run:
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
inline class MyInlineClass(val i: Int)
data class MyDataClass(val i: MyInlineClass)
fun main() {
val mapper = jacksonObjectMapper()
val json = mapper.writeValueAsString(MyDataClass(MyInlineClass(1)))
println(json)
val deserialized = mapper.readValue<MyDataClass>(json)
println(deserialized)
}
Expected behavior
The JSON is properly deserialized.
Actual behavior
{"i":1}
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `MyDataClass` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"i":1}"; line: 1, column: 2]
Or, if there are multiple fields in the data class:
{"i":1,"j":1}
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `MyDataClass` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"i":1,"j":1}"; line: 1, column: 2]
Versions
Kotlin: 1.4.21
Jackson-module-kotlin: 2.12.1
Jackson-databind: 2.12.1
Additional context
I've tried to dig through this and discovered that adding an inline class-typed property causes some constructor changes - the compiled data class has two constructors:
// access flags 0x2
private <init>(I)V
// access flags 0x1001
public synthetic <init>(ILkotlin/jvm/internal/DefaultConstructorMarker;)V
The second one is ignored by Jackson because it's synthetic. The first one is correctly discovered, however, that constructor is invisible through Kotlin reflection, so Jackson-module-kotlin fails to discover the parameter names and is not able to use that constructor.
The problem can not be worked around by using @JsonProperty
because Kotlin compiler places it onto the parameters of the synthetic constructor, and no annotations appear on the private constructor.