Skip to content

Deserialisation to polymorphic class using JsonTypeInfo.Id.DEDUCTION and JacksonXmlElementWrapper annotation fails #514

Open
@AlexiosP

Description

@AlexiosP

XML parsing fails when mapping to polymorphic class using JsonTypeInfo.Id.DEDUCTION and a property of the class is a flat list of elements where @JacksonXmlElementWrapper(useWrapping = false) is used.

Reproducable example in kotlin showcasing the issue

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator
import com.fasterxml.jackson.module.kotlin.KotlinFeature
import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.junit.jupiter.api.Test

class XmlTest {

    private val somexml = """
    <?xml version="1.0" encoding="UTF-8"?>
    <RootTag>
        <Response>
            <SomeData>hello</SomeData>
            <Detail>
                <Text>hello world</Text>
            </Detail>
        </Response>
    </RootTag>
    """.trimIndent()

    @JsonTypeInfo(
        use = JsonTypeInfo.Id.DEDUCTION
    )
    @JsonSubTypes(
        JsonSubTypes.Type(value = Response.Success::class),
        JsonSubTypes.Type(value = Response.Error::class)
    )
    sealed class Response {
        data class Success(
            @JsonProperty("SomeData")
            val data: String,

            @JacksonXmlProperty(localName = "Detail")
            @JacksonXmlElementWrapper(useWrapping = false)
            val details: List<NestedClass>,
        ) : Response()

        data class Error(@JsonProperty("ErrCd") val errorCode: String) : Response()
    }

    data class NestedClass(@JsonProperty("Text") val text: String)
    data class WholeResponse(@JsonProperty("Response") val response: Response)
    data class WholeResponseWithoutDeduction(@JsonProperty("Response") val response: Response.Success)

    @Test
    fun `parse xml using deduction`() {
        // This test fails although it should pass
        ConfiguredXmlMapper.readValue(somexml, WholeResponse::class.java)
    }

    @Test
    fun `parse xml without using deduction`() {
        // This test passes which is normal
        ConfiguredXmlMapper.readValue(somexml, WholeResponseWithoutDeduction::class.java)
    }
}

object ConfiguredXmlMapper : XmlMapper() {
    init {
        registerModule(KotlinModule.Builder()
            .withReflectionCacheSize(512)
            .configure(KotlinFeature.NullToEmptyCollection, false)
            .configure(KotlinFeature.NullToEmptyMap, false)
            .configure(KotlinFeature.NullIsSameAsDefault, false)
            .configure(KotlinFeature.SingletonSupport, false)
            .configure(KotlinFeature.StrictNullChecks, false)
            .build())
    }
}

Using these related dependencies

dependencies {
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.2")
    implementation("com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.13.2")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
    implementation("com.fasterxml.jackson.module:jackson-modules-java8:2.13.2")
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    has-failing-testIndicates that there exists a test case (under `failing/`) to reproduce the issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions