Fasterxml Jackson - deserializacja obiektu który czasami jest listą

0

Cześć,
Mam problem z deserializacją xmla który posiada numer telefonu (a czasami listę numerów):

<telNumber><value>1234567</value><id>3433</id></telNumber>

lub listę:

<telNumber><telNumber><value>1234567</value><id>3433</id></telNumber><telNumber><value>77777</value><id>123</id></telNumber></telNumber>

Klasa ma ustawione to pole jako lista. Przez co w przypadku wysłania pojedynczego obiektu dostaje exception:

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.test.TelNumber` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1234567')
 at [Source: UNKNOWN; line: 1, column: 1487] (through reference chain: com.test.Application["person"]->java.util.ArrayList[0]->com.test.Person["telNumber"]->java.util.ArrayList[0])

Wiecie czy jest szansa skonfigurować fasterxml żeby radził sobie z pojedynczymi obiektami? Znalazłem rozwiązanie dla jsonów, ale potrzebuję dla xml.

1

Problem w tym ze masz tag który może się zachowywać różnie. Zauważ że ten tag telnumber może albo mieć te swoje atrybuty, albo zanieżdżone kolejne tagi telnumber. To jest w ogóle legalne w XMLu?

2

JAXB powinno to ogarnąć, ale Jackson też da radę. Wystarczy DTO dla phone number zamodelować jako kompozyt i potem przemapować wszystko do "wyczyszczonego" DTO. Przydaje się @JacksonXmlElementWrapper. Na szybko w Kotlinie wyszło mi tak:

import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty

const val singleXML = """
    <root>
        <telnumber>
            <value>1234567</value>
            <id>3433</id>
        </telnumber>
    </root>
"""

const val multiXML = """
    <root>
        <telnumber>
            <telnumber>
                <value>1234567</value>
                <id>3433</id>
            </telnumber>
            <telnumber>
                <value>77777</value>
                <id>123</id>
            </telnumber>
        </telnumber>
    </root>
"""

class PhoneNumberComposite(var value: String = "") {

    var id: String = ""

    @JacksonXmlElementWrapper(localName = "telnumber", useWrapping = false)
    @JacksonXmlProperty(localName = "telnumber")
    var phoneNumbers: List<PhoneNumberComposite> = emptyList()

    fun asPhoneNumbers(): Sequence<PhoneNumber> =
        if (phoneNumbers.isEmpty()) {
            sequenceOf(PhoneNumber(value, id))
        } else {
            phoneNumbers.asSequence()
                .flatMap { it.asPhoneNumbers() }
        }

}

data class PhoneNumber(
    val value: String,
    val id: String
)

class Root {

    @JacksonXmlProperty(localName = "telnumber")
    var phoneNumberComposite: PhoneNumberComposite? = null

    val allPhoneNumbers: List<PhoneNumber>
        get() = phoneNumberComposite?.asPhoneNumbers()?.toList() ?: emptyList()
}



fun main() {
    val mapper = XmlMapper()
    val rootFromSingle = mapper.readValue(singleXML, Root::class.java)
    val rootFromMulti = mapper.readValue(multiXML, Root::class.java)
    println(rootFromSingle.allPhoneNumbers)
    println(rootFromMulti.allPhoneNumbers)
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1