Deserializing objects with duplicate keys
bgrozev opened this issue · comments
I ran into some strange behavior deserializing with @JsonTypeInfo
and I wanted to know if it's expected. When the parsed object has duplicate values for the same key, sometime the last value is picked up, but sometimes it's the first. Here's an example I managed to isolate (if I have a val
declared in the constructor it picks up the first, otherwise it picks up the last):
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import io.kotest.core.spec.style.ShouldSpec
private val mapper = jacksonObjectMapper()
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "name", include = JsonTypeInfo.As.EXISTING_PROPERTY)
@JsonSubTypes(
JsonSubTypes.Type(value = ClassWithVal::class, name = "ClassWithVal"),
JsonSubTypes.Type(value = ClassWithoutVal::class, name = "ClassWithoutVal"),
)
private open class Base {
val prop: String? = null
fun toJson(): String = mapper.writeValueAsString(this)
}
private class ClassWithVal(val v: String? = null): Base()
private class ClassWithoutVal: Base()
class JacksonTest : ShouldSpec() {
init {
context("ClassWithVal uses the first value for a property") {
val s = """
{
"name": "ClassWithVal",
"prop": "first",
"prop": "second",
"prop": "third"
}
"""
val parsed: ClassWithVal = mapper.readValue(s)
println("class=${parsed::class.java.simpleName} prop=${parsed.prop}") // prop is "first"
println("class=${parsed::class.java.simpleName} serialized=${parsed.toJson()}")
}
context("ClassWithoutVal uses the last value for a property") {
val s = """
{
"name": "ClassWithoutVal",
"prop": "first",
"prop": "second",
"prop": "third"
}
"""
val parsed: ClassWithoutVal = mapper.readValue(s)
println("class=${parsed::class.java.simpleName} prop=${parsed.prop}") // prop is "third"
println("class=${parsed::class.java.simpleName} serialized=${parsed.toJson()}")
}
}
}
I don't think the JSON spec (ECMA-404/) says anything about duplicate keys, but according to the the ECMAScript spec (ECMA-262) the last value should be taken and this is how the browsers I tested behave.
I think the differences come from a different value for BeanDeserializer._vanillaProcessing
. I've tested with jackson 2.14.1. Is this expected behavior? Should jackson always behave like javascript in this regard for better interop? I know it's a corner case and duplicate keys shouldn't be used.
The kotlin-module
is not involved in parsing JSON
.
Please rewrite the sample code in Java
and submit the issue to the appropriate repository, such as databind
.
This issue is closed.
Note that it is possible to enforce uniqueness of Object property names for parser, by configuring JsonFactory
with StreamReadFeature.STRICT_DUPLICATE_DETECTION
enabled.
If this is not done, then all duplicate values are incrementally used for "simple" properties (when setting values).