XML Jackson 与 Kotlin 中未真正嵌套的 XML 绑定

问题描述

我有一个“不好看”的 XML。 (是的,它是有效的,但与我通常得到的以及我知道如何处理的不同)

通常我希望看到类似的内容

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

我的 abvoe XML 数据类如下所示:

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

data class Book(
    .....
    )

但不幸的是,我们传入的 XML 如下所示:

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

所以我正在努力将它与普通的 Jackson Mapper 匹配,而不使用自定义的映射器。 (如果我需要定制一个,它应该是什么样子的?)

我的映射器如下所示:

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)

解决方法

要反序列化这样的 XML,您可以使用两个技巧:

  • 明确关闭 List<Book> 的包装,因为他们直接坐在 Parent
  • 使用自定义 setter 解决引擎限制,这是 GitHub 问题 #275
  • 中提出的解决方案

更具体地说,您可以对类进行注释以允许反序列化您的示例:

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

我上传了一个完整的示例,它使用您的 Jackson 配置和示例 XML,作为 Kotlin 脚本到 GitHub Gist。它还在底部显示了反序列化的 XML 的输出。