jackson-module-kotlin: @JacksonXmlElementWrapper doesn’t work with data classes
I want to mapping a xml using kotlin data class, my xml wrapper a list of elements in a wapper node.
Xml example: <MyPojo><elements><element value="e1"/></elements></MyPojo>
Here the <element> node is a collection, wrapped in the <elements> node.
When I use the data class to map the ‘MyPojo’ and ‘element’ object and I annotated the collection property with @JacksonXmlElementWrapper
and @JacksonXmlProperty
it doesn’t work when I try to deserialise form xml.
The exception is:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid definition for property elements
(of type MyDataPojo
): Could not find creator property with name ‘elements’ (known Creator properties: [element])
I use :
- Java 1.8
- Kotlin 1.2.31
- Jackson: 2.9.5
I have write a test to compare the mapping using the standard class and the data class. The standard class test passed, the data class test failed.
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.annotation.JacksonXmlRootElement
import com.fasterxml.jackson.module.kotlin.KotlinModule
import junit.framework.Assert.assertEquals
import org.junit.Test
/**
* Mapping the Xml with standard classes
*/
@JacksonXmlRootElement(localName = "MyPojo")
class MyPojo {
@JacksonXmlElementWrapper(localName = "elements")
@JacksonXmlProperty(localName = "element")
var list: List<MyElement>? = null
}
/**
* Mapping the Xml with standard classes
*/
class MyElement {
@JacksonXmlProperty(localName = "value", isAttribute = true)
var value: String? = null
}
/**
* Mapping the Xml with data classes
*/
@JacksonXmlRootElement(localName = "MyPojo")
data class MyDataPojo (
@JacksonXmlElementWrapper(localName = "elements")
@JacksonXmlProperty(localName = "element")
val list
: List<MyDataElement>
)
/**
* Mapping the Xml with data classes
*/
data class MyDataElement (
@JacksonXmlProperty(localName = "value", isAttribute = true)
var value: String
)
class TextKotlinXmlWrapper {
// xml example I want to deserialize
val xml = """<MyPojo><elements><element value="e1"/></elements></MyPojo>"""
val mapper = XmlMapper()
.registerModule(KotlinModule())
@Test
@Throws(Exception::class)
fun test_class() {
// I create a pojo from the xml using the standard classes
val pojoFromXml = mapper.readValue(xml, MyPojo::class.java)
//I create a xml from the pojo
val xmlFromPojo = mapper.writeValueAsString(pojoFromXml)
// I compare the original xml with the xml generated from the pojo
assertEquals(xml, xmlFromPojo)
}
@Test
@Throws(Exception::class)
fun test_data_class() {
// I create a pojo from the xml using the data classes
val pojoFromXml = mapper.readValue(xml, MyDataPojo::class.java)
//I create a xml from the pojo
val xmlFromPojo = mapper.writeValueAsString(pojoFromXml)
// I compare the original xml with the xml generated from the pojo
assertEquals(xml, xmlFromPojo)
}
}
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 15
- Comments: 34 (15 by maintainers)
Commits related to this issue
- not resolvable error github #153 — committed to FasterXML/jackson-module-kotlin by apatrida 6 years ago
Hi guys, Just in case it helps somebody else, I found a workaround by combining the things mentioned here with a property backed up with a constructor field.
This allow us to still use a data class and avoid writing getters/setters/hashcode/equals.
It is not too elegant, but it works fine so hopefully it will help.
Thanks guys for helping me figuring it out!
FYI, from Kotlin you can specify where the annotation lands, using the
get:
orfield:
prefixes between the@
and the annotation name. So the following should work:so @cowtowncoder
JacksonXmlElementWrapper
cannot be on a parameter therefore no property name ofelements
gets added to the creator parameter, and insteadJacksonXmlProperty
ends up on the parameter which is the wrong name. So then the mapper tries to pass inelements
but the parameter appears to be propertyelement
I have created the following test with the 2.15 branch up to date. These were created based on the tests shared in https://github.com/FasterXML/jackson-dataformat-xml/issues/517 .
All of these tests fail with the same stack trace as the original, with or without
kotlin-module
. Therefore, we can assume that this is not a problem withKotlin
orkotlin-module
, but simply that theJacksonXmlElementWrapper
does not work for thecreator
parameter.If you have evidence that
Kotlin
orkotlin-module
is the cause, please submit a new issue.For all the exhausted gogglers out there who try to fix this issue: at this day Jackson XML de-serialization is still bugged when paired with Kotlin Data-Classes => do yourself the favor and use classes instead.
While the solution presented here works well for serialization, I’ve found that in jackson 2.9.8 it breaks for de-serialization due to the KotlinValueInstantiator trying to find the _elements parameter in the xml. The solution is to simply give the _elements parameter a default value:
De-serializes properly with Json too, even with polymorphic elements.