
jackson是一个功能强大的java库,用于处理json和xml数据。通过jackson-dataformat-xml模块,jackson能够方便地将xml字符串或文件反序列化为java对象,或将java对象序列化为xml。然而,在处理复杂的xml结构,特别是包含列表(list)类型数据时,可能会遇到一些挑战,需要借助特定的注解来指导jackson的解析行为。
当尝试将一个包含嵌套列表的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<FINSTA03Bean> FINSTA03BeanList; // 期望解析为列表
}
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Setter @NoArgsConstructor
public class FINSTA03Bean {
@JsonProperty("S28_CISLO_VYPISU")
String S28_CISLO_VYPISU;
String S25_CISLO_UCTU; // 假设此字段在XML中存在,但未显式注解
}以及对应的XML数据片段:
<?xml version="1.0" encoding="windows-1250"?>
<FINSTA>
<STA_VER>01.0000</STA_VER>
<FINSTA03>
<S28_CISLO_VYPISU>10</S28_CISLO_VYPISU>
</FINSTA03>
<FINSTA03>
<S28_CISLO_VYPISU>20</S28_CISLO_VYPISU>
</FINSTA03>
</FINSTA>当使用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中的<FINSTA03>元素的内容(例如10)直接反序列化为一个FINSTA03Bean对象,而不是将其识别为一个包含FINSTA03Bean对象的列表。由于FINSTA03Bean没有接受单个字符串参数的构造函数或工厂方法,导致反序列化失败。
Jackson在处理XML时,对于集合类型的字段,默认行为是期望有一个包装元素(wrapper element)来包含列表中的所有项。例如,如果XML结构是:
<FINSTA03List> <!-- 包装元素 -->
<FINSTA03>...</FINSTA03>
<FINSTA03>...</FINSTA03>
</FINSTA03List>而我们的XML中<FINSTA03>元素直接作为父元素<FINSTA>的子元素重复出现,并没有一个额外的包装元素来明确表示这是一个列表。在这种情况下,Jackson会误将第一个<FINSTA03>元素视为一个独立的、非列表的字段,并尝试将其内容(例如<S28_CISLO_VYPISU>10</S28_CISLO_VYPISU>)作为一个字符串值来构建FINSTA03Bean,从而引发MismatchedInputException。
为了正确指导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<FINSTA03Bean> 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结构。它会知道<FINSTA03>元素并不是一个单一的字符串值,而是列表中的一个对象实例,并且会逐个解析所有同名的<FINSTA03>元素,并将它们收集到FINSTA03BeanList中。
<FINSTA03List>
<FINSTA03>...</FINSTA03>
<FINSTA03>...</FINSTA03>
</FINSTA03List>那么在FINSTABean中,FINSTA03BeanList字段应该这样注解:
@JacksonXmlElementWrapper(localName = "FINSTA03List") // 包装元素名称 @JacksonXmlProperty(localName = "FINSTA03") // 列表项元素名称 List<FINSTA03Bean> FINSTA03BeanList;
此时,useWrapping默认为true,或者可以显式设置为true。
在Jackson XML反序列化过程中,处理列表类型数据是一个常见的陷阱。MismatchedInputException通常是由于Jackson未能正确识别XML中的列表结构导致的。通过巧妙地运用@JacksonXmlElementWrapper和@JacksonXmlProperty这两个Jackson XML模块特有的注解,我们可以精确地指导Jackson如何解析复杂的XML结构,特别是当列表元素没有显式包装器时,使用@JacksonXmlElementWrapper(useWrapping = false)能够有效解决问题。理解并正确应用这些注解,是实现高效、健壮Jackson XML数据处理的关键。
以上就是Jackson XML 反序列化深度指南:处理列表类型数据的常见陷阱与解决方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号