Search code examples
xmlkotlinjackson-dataformat-xml

Jackson Xml parsing chooses attribute instead of tag


I have this xml file:

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<nms:nmsDocument xmlns:nms="http://www.nms.com/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <nms:nmsObject xsi:type="nms:Test">
        <nms:type>
            <nms:domainValue>
                <nms:name>"Hello"</nms:name>
            </nms:domainValue>
        </nms:type>
    </nms:nmsObject>
</nms:nmsDocument>

I need to parse it and get the value of <nms:name> which is "Hello".

I've written these classes:

@JacksonXmlRootElement(localName = "nmsDocument", namespace = "nms")
@JsonIgnoreProperties(ignoreUnknown = true)
data class NmsDocument(
    @JacksonXmlProperty(localName = "nmsObject", namespace = "nms")
    val nmsObject: NmsObject
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class NmsObject(
    @JacksonXmlProperty(localName = "type", namespace = "nms", isAttribute = false)
    val type: Type
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class Type(
    @JacksonXmlProperty(localName = "domainValue", namespace = "nms")
    val domainValue: DomainValue
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class DomainValue(
    @JacksonXmlProperty(localName = "name", namespace = "nms")
    val name: String
)

The error is: Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.example.demo.Type (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('nms:Test') at [Source: (StringReader); line: 3, column: 5] (through reference chain: com.example.demo.NmsDocument["nmsObject"]->com.example.demo.NmsObject["type"])

It tries to parse attribute xsi:type from <nms:nmsObject> tag instead of <nms:type> tag which is in <nms:nmsObject>. Is there any solution to that? Nothing helps to fix it.

It is advisable to implement this using the Jackson lib


Solution

  • You can write a custom deserializer for NmsObject class. Like this:

    class NmsObjectDeserializer : StdDeserializer<NmsObject>(NmsObject::class.java) {
        override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NmsObject {
            val node: JsonNode = parser.codec.readTree(parser)
            val type = parser.codec.treeToValue(node.get("type").get(1), Type::class.java)
            return NmsObject(type)
        }
    }
    

    and

    @JsonDeserialize(using = NmsObjectDeserializer::class)
    @JsonIgnoreProperties(ignoreUnknown = true)
    data class NmsObject(
        @JacksonXmlProperty(localName = "type", namespace = "nms", isAttribute = false)
        val type: Type
    )
    

    You should use 1 in this line node.get("type").get(1), the 1st item is the attribute.