-
-
Notifications
You must be signed in to change notification settings - Fork 180
Closed
Labels
Description
Search before asking
- I searched in the issues and found nothing similar.
- I searched in the issues of databind and other modules used and found nothing similar.
- I have confirmed that the problem only occurs when using Kotlin.
Describe the bug
When trying to deserialize a CSV into a target class that contains a field that is a value class and
the ObjectMapper has a CsvSchema that has column reordering enabled, Jackson will produce the following exception, seemingly ignoring the @JsonProperty annotations:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "BAR_HEADER" (class net.marvk.FooWithValueClass), not marked as ignorable (2 known properties: "bar", "baz"])
at [Source: (StringReader); line: 2, column: 41] (through reference chain: net.marvk.FooWithValueClass["BAR_HEADER"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1153)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:2241)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1793)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperties(BeanDeserializerBase.java:1743)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:546)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1493)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.databind.MappingIterator.nextValue(MappingIterator.java:283)
at com.fasterxml.jackson.databind.MappingIterator.readAll(MappingIterator.java:323)
at com.fasterxml.jackson.databind.MappingIterator.readAll(MappingIterator.java:309)
at net.marvk.MainKt.main(Main.kt:20)
at net.marvk.MainKt.main(Main.kt)
This issue does not exist for data classes or if column reordering is disabled.
data class |
value class |
|
|---|---|---|
Column Reordering false |
✔ | ✔ |
Column Reordering true |
✔ | ❌ |
Related to #768.
To Reproduce
Please see the cloneable, runnable MCVE here.
The MCVE tests demonstrate that data classes work whether column reordering is enabled or not, and value classes work with column reordering disabled.
Main.kt
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.ObjectReader
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.dataformat.csv.CsvMapper
import com.fasterxml.jackson.dataformat.csv.CsvParser
import com.fasterxml.jackson.dataformat.csv.CsvSchema
import com.fasterxml.jackson.module.kotlin.kotlinModule
/**
* Just show me the bug!
*/
fun main() {
createReader<FooWithValueClass>(true)
.readValues<FooWithValueClass>(CSV)
.readAll()
}
val CSV = """
BAR_HEADER,BAZ_HEADER
bar1,baz1
""".trimIndent()
val MAPPER =
CsvMapper
.builder()
.addModule(
SimpleModule()
.addDeserializer(BazValueClass::class.java, object : JsonDeserializer<BazValueClass>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = BazValueClass(p.valueAsString)
})
.addDeserializer(BazDataClass::class.java, object : JsonDeserializer<BazDataClass>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = BazDataClass(p.valueAsString)
})
)
.addModule(kotlinModule())
.build()
inline fun <reified T> createSchema(columnReordering: Boolean): CsvSchema =
MAPPER
.schemaFor(T::class.java)
.withColumnReordering(columnReordering)
.withHeader()
inline fun <reified T> createReader(columnReordering: Boolean): ObjectReader =
MAPPER
.readerFor(T::class.java)
.with(CsvParser.Feature.WRAP_AS_ARRAY)
.with(createSchema<T>(columnReordering))
data class FooWithValueClass(
@JsonProperty("BAR_HEADER")
val bar: String?,
@JsonProperty("BAZ_HEADER")
val baz: BazValueClass?,
)
data class FooWithDataClass(
@JsonProperty("BAR_HEADER")
val bar: String?,
@JsonProperty("BAZ_HEADER")
val baz: BazDataClass?,
)
@JvmInline
value class BazValueClass(val value: String)
data class BazDataClass(val value: String)Test.kt
import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.matchers.collections.shouldContainExactly
import org.junit.jupiter.api.Test
class Test {
@Test
fun valueClassWithoutColumnReordering() {
val actual = shouldNotThrowAny {
createReader<FooWithValueClass>(false)
.readValues<FooWithValueClass>(CSV)
.readAll()
}
actual shouldContainExactly listOf(FooWithValueClass("bar1", BazValueClass("baz1")))
}
/**
* This one is broken!
*/
@Test
fun valueClassWithColumnReordering() {
val actual = shouldNotThrowAny {
createReader<FooWithValueClass>(true)
.readValues<FooWithValueClass>(CSV)
.readAll()
}
actual shouldContainExactly listOf(FooWithValueClass("bar1", BazValueClass("baz1")))
}
@Test
fun dataClassWithoutColumnReordering() {
val actual = shouldNotThrowAny {
createReader<FooWithDataClass>(false)
.readValues<FooWithDataClass>(CSV)
.readAll()
}
actual shouldContainExactly listOf(FooWithDataClass("bar1", BazDataClass("baz1")))
}
@Test
fun dataClassWithColumnReordering() {
val actual = shouldNotThrowAny {
createReader<FooWithDataClass>(true)
.readValues<FooWithDataClass>(CSV)
.readAll()
}
actual shouldContainExactly listOf(FooWithDataClass("bar1", BazDataClass("baz1")))
}
}Expected behavior
Jackson deserializes the CSV without issues just like with data class fields or with column reordering disabled.
Versions
Kotlin: 1.9.23
Jackson-module-kotlin: 2.17.1
Jackson-databind: 2.17.1
Additional context
No response