Joergi
Joergi

Reputation: 1593

XML Jackson binding with a not really nested XML in Kotlin

I have "a not nice looking" XML. (yes it's valid but different to the ones I normally get and where I know how to deal with it)

Normally I wanted so see something like:

<Parent....>
  <Books>
    <Book ...>
    <Book ...>
    <Book ...>
  </Books>
</Parent> 

my Data Class for the abvoe XML would look like this:

data class Parent(
    @JacksonXmlProperty(localName = "Books")
    @JacksonXmlElementWrapper
    var books: List<Book> 
)

data class Book(
    .....
    )

but unfortunately our incoming XML looks like this:

<Parent....>
    <Book ...>
    <Something>
    <Book ....>
    <Something>
    <Book ...>
</parent> 

So I'm struggling with matching this with a normal Jackson Mapper, without using a custom one. (and if I need a custom one, how it needs to look like?)

My mapper looks like this:

val kotlinModule: KotlinModule = KotlinModule.Builder()
                .strictNullChecks(false)
                .nullIsSameAsDefault(true) // needed, else it will break for null https://github.com/FasterXML/jackson-module-kotlin/issues/130#issuecomment-546625625
                .build()

val xmlMapper =
                XmlMapper(JacksonXmlModule())
                        .registerModule(kotlinModule)
                        .registerModule(JavaTimeModule())
                        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // to parse the dates as LocalDate, else parsing error
                        .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
                        .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
                        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)

Upvotes: 0

Views: 1392

Answers (1)

Heiko Rothe
Heiko Rothe

Reputation: 331

For deserializing such an XML there's two tricks you can use:

  • Explicitly turning off wrapping for your List<Book>, as they are sitting directly in the Parent
  • Working around engine limitations with a custom setter, a solution proposed in GitHub issue #275

More specifically, you can annotate your classes as such to allow deserialization of your example:

@JacksonXmlRootElement
class Parent {
    @JacksonXmlProperty(localName = "Book")
    @JacksonXmlElementWrapper(useWrapping = false)
    var books: List<Book> = ArrayList()
        set(value) {
            field = books + value
        }
}

data class Book(
    @JacksonXmlProperty
    val title: String
)

I uploaded a full example, which uses your Jackson config and sample XML, as a Kotlin Script to GitHub Gist. It also shows the output of the deserialized XML at the bottom.

Upvotes: 1

Related Questions