Jackson XML解析误取属性而非标签的问题排查与解决
问题场景
给定如下XML文件:
<?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>
需要解析该XML获取<nms:name>标签的值"Hello",编写了以下Kotlin数据类:
NmsDocument类
@JacksonXmlRootElement(localName = "nmsDocument", namespace = "nms") @JsonIgnoreProperties(ignoreUnknown = true) data class NmsDocument( @JacksonXmlProperty(localName = "nmsObject", namespace = "nms") val nmsObject: NmsObject )
NmsObject类
@JsonIgnoreProperties(ignoreUnknown = true) data class NmsObject( @JacksonXmlProperty(localName = "type", namespace = "nms", isAttribute = false) val type: Type )
Type类
@JsonIgnoreProperties(ignoreUnknown = true) data class Type( @JacksonXmlProperty(localName = "domainValue", namespace = "nms") val domainValue: DomainValue )
DomainValue类
@JsonIgnoreProperties(ignoreUnknown = true) data class DomainValue( @JacksonXmlProperty(localName = "name", namespace = "nms") val name: String )
运行时出现错误:
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"])
问题根源:Jackson默认会将xsi:type属性识别为多态类型标识,错误地把它映射到了NmsObject类的type字段,而非解析目标内部的<nms:type>标签。
解决方案
方案一:显式忽略xsi:type属性
修改NmsObject类,显式声明并忽略xsi:type属性,确保type字段正确映射到内部标签:
@JsonIgnoreProperties(ignoreUnknown = true) data class NmsObject( @JacksonXmlProperty(localName = "type", namespace = "nms") val type: Type, // 声明xsi:type属性并标记为忽略 @JacksonXmlProperty(localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance") @JsonIgnore val xsiType: String? = null )
方案二:全局禁用xsi:type自动识别
在创建XmlMapper实例时,配置禁用USE_XSI_TYPE特性,这样Jackson就不会将xsi:type视为多态标识,结合@JsonIgnoreProperties(ignoreUnknown = true)会自动忽略该属性:
val xmlMapper = XmlMapper.builder() .disable(MapperFeature.USE_XSI_TYPE) .build()
验证
使用上述任一方案修改后,重新解析XML即可正常获取<nms:name>标签的值"Hello"。
内容的提问来源于stack exchange,提问作者Semyon Volokh

