FasterXML / jackson-module-kotlin

Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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).