
Jackson XML反序列化概述
jackson是一个功能强大的java库,用于处理json和xml数据。通过jackson-dataformat-xml模块,jackson能够方便地将xml字符串或文件反序列化为java对象,或将java对象序列化为xml。然而,在处理复杂的xml结构,特别是包含列表(list)类型数据时,可能会遇到一些挑战,需要借助特定的注解来指导jackson的解析行为。
列表类型数据反序列化常见问题:MismatchedInputException
当尝试将一个包含嵌套列表的XML文件反序列化为Java对象时,如果未正确配置,Jackson可能会抛出com.fasterxml.jackson.databind.exc.MismatchedInputException异常,并伴随“Cannot construct instance of YourBean (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('...')”的错误信息。
示例场景:
假设有以下Java数据传输对象(DTO):
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Setter @NoArgsConstructor
public class FINSTABean {
@JsonProperty("STA_VER")
String STA_VER;
@JsonProperty("FINSTA03")
List FINSTA03BeanList; // 期望解析为列表
}
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Setter @NoArgsConstructor
public class FINSTA03Bean {
@JsonProperty("S28_CISLO_VYPISU")
String S28_CISLO_VYPISU;
String S25_CISLO_UCTU; // 假设此字段在XML中存在,但未显式注解
} 以及对应的XML数据片段:
01.0000 10 20
当使用XmlMapper尝试反序列化时:
XmlMapper xmlMapper = new XmlMapper(); FINSTABean bean = xmlMapper.readValue(file, FINSTABean.class);
可能会遇到如下错误:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.lelifin.alfa.parsers.csob_xml.FINSTA03Bean` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('10')
at [Source: (File); line: 5, column: 29] (through reference chain: com.lelifin.alfa.parsers.csob_xml.FINSTABean["FINSTA03"]->java.util.ArrayList[0])这个错误表明Jackson尝试将XML中的
问题根源:Jackson对XML列表结构的默认处理
Jackson在处理XML时,对于集合类型的字段,默认行为是期望有一个包装元素(wrapper element)来包含列表中的所有项。例如,如果XML结构是:
... ...
而我们的XML中
解决方案:使用@JacksonXmlElementWrapper和@JacksonXmlProperty
为了正确指导Jackson处理这种扁平化的列表结构,我们需要使用jackson-dataformat-xml提供的特定注解:@JacksonXmlElementWrapper和@JacksonXmlProperty。
@JacksonXmlElementWrapper(useWrapping = false) 这个注解用于指示Jackson如何处理集合或数组类型的字段。当useWrapping设置为false时,它告诉Jackson该集合的元素不是由一个额外的包装XML元素所包裹,而是直接作为父元素的子元素出现。这正是我们当前XML结构所需要的。
@JacksonXmlProperty(localName = "...")@JacksonXmlProperty是Jackson XML模块特有的注解,用于将Java对象的字段映射到XML元素或属性。它类似于@JsonProperty,但专为XML设计,提供了更多XML相关的控制。localName属性用于指定XML元素的名称。
修正后的DTO代码:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Setter @NoArgsConstructor
public class FINSTABean {
@JacksonXmlProperty(localName = "STA_VER") // 使用JacksonXmlProperty映射XML元素名
String STA_VER;
// 关键修正:
// 1. @JacksonXmlProperty(localName = "FINSTA03"):将Java字段FINSTA03BeanList映射到XML元素FINSTA03。
// 2. @JacksonXmlElementWrapper(useWrapping = false):指示FINSTA03BeanList的元素不是由一个额外的包装元素包裹,
// 而是FINSTA03元素本身就代表列表中的一个项。
@JacksonXmlProperty(localName = "FINSTA03")
@JacksonXmlElementWrapper(useWrapping = false)
List FINSTA03BeanList;
}
@JsonIgnoreProperties(ignoreUnknown = true) // 仍然保留,忽略XML中Java对象未定义的字段
@Getter @Setter @NoArgsConstructor
public class FINSTA03Bean {
@JacksonXmlProperty(localName = "S28_CISLO_VYPISU")
String S28_CISLO_VYPISU;
@JacksonXmlProperty(localName = "S25_CISLO_UCTU") // 如果XML中存在此元素,也应明确映射
String S25_CISLO_UCTU;
} 通过上述修改,Jackson现在能够正确地识别FINSTA03BeanList字段对应的XML结构。它会知道
注意事项与最佳实践
- @JsonProperty vs. @JacksonXmlProperty: 尽管@JsonProperty在某些情况下也能用于XML映射,但@JacksonXmlProperty是Jackson XML模块提供的更专业、更强大的注解,建议在处理XML时优先使用它,尤其是在需要精细控制XML元素或属性映射时。
-
useWrapping = true 的场景: 如果XML结构中确实存在一个包装元素,例如:
... ... 那么在FINSTABean中,FINSTA03BeanList字段应该这样注解:
@JacksonXmlElementWrapper(localName = "FINSTA03List") // 包装元素名称 @JacksonXmlProperty(localName = "FINSTA03") // 列表项元素名称 List
FINSTA03BeanList; 此时,useWrapping默认为true,或者可以显式设置为true。
- 忽略未知属性: @JsonIgnoreProperties(ignoreUnknown = true) 是一个非常有用的注解,它指示Jackson在反序列化时忽略XML中存在但Java对象中没有对应字段的元素或属性,这可以提高代码的健壮性。
- Lombok集成: @Getter, @Setter, @NoArgsConstructor等Lombok注解与Jackson完美兼容,它们在编译时生成了必要的getter/setter方法和无参构造函数,满足Jackson反序列化的要求。
- XML编码: XML文件头部的encoding="windows-1250"指定了字符编码。在Java中读取文件时,确保使用正确的编码方式,例如new InputStreamReader(new FileInputStream(file), "windows-1250"),以避免乱码问题。
总结
在Jackson XML反序列化过程中,处理列表类型数据是一个常见的陷阱。MismatchedInputException通常是由于Jackson未能正确识别XML中的列表结构导致的。通过巧妙地运用@JacksonXmlElementWrapper和@JacksonXmlProperty这两个Jackson XML模块特有的注解,我们可以精确地指导Jackson如何解析复杂的XML结构,特别是当列表元素没有显式包装器时,使用@JacksonXmlElementWrapper(useWrapping = false)能够有效解决问题。理解并正确应用这些注解,是实现高效、健壮Jackson XML数据处理的关键。










