
在现代Java应用开发中,尤其是在Spring Boot生态系统中,处理XML数据是常见的任务之一。Jackson作为一款功能强大的JSON处理库,也提供了对XML数据格式的支持,通过jackson-dataformat-xml模块,可以方便地将XML文档映射到Java对象,实现反序列化(从XML到Java)和序列化(从Java到XML)。然而,对于初学者而言,在处理包含多个同名子元素的XML列表时,可能会遇到仅能解析最后一个元素的问题。本文将详细讲解如何正确配置Jackson注解来解决这一挑战。
考虑以下XML结构,它包含一个根元素<cpe-list>,以及多个<cpe-item>子元素:
<?xml version="1.0" encoding="UTF-8" ?>
<cpe-list>
<cpe-item name="John">
<title>xmlread</title>
</cpe-item>
<cpe-item name="Jack">
<title>testtitle</title>
</cpe-item>
</cpe-list>我们的目标是将这个XML文件解析成一个Java对象,其中<cpe-list>对应一个主类,而内部的多个<cpe-item>则被收集到一个列表中。
最初尝试的Java模型可能如下:
CpeItem.java
package com.dependency.demo;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
@Data
@JacksonXmlRootElement(localName = "cpe-item")
public class CpeItem {
@JacksonXmlProperty(localName = "name", isAttribute = true)
private String name;
private String title;
}CpeList.java (初始尝试)
package com.dependency.demo;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
@Data
@JacksonXmlRootElement(localName = "cpe-list")
public class CpeList {
// 错误地将CpeItem定义为单个对象而非列表
@JacksonXmlElementWrapper(localName = "cpe-item")
private CpeItem cpeItems; // 这里是关键错误
}使用上述CpeList模型进行解析时,由于cpeItems被定义为单个CpeItem对象,Jackson在遇到多个<cpe-item>标签时,会不断用新的值覆盖旧的值,最终只保留最后一个<cpe-item>的数据。这是初学者在处理XML列表时常遇到的问题。
为了正确解析XML,我们需要理解Jackson XML模块提供的一些关键注解:
问题的核心在于CpeList类中cpeItems字段的定义以及@JacksonXmlElementWrapper注解的误用。
基于上述分析,我们对CpeList类进行如下修正:
CpeItem.java (保持不变)
package com.dependency.demo;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
import java.util.List; // 尽管CpeItem本身不直接使用List,但为了完整性,这里保留了引入。实际无需
@Data
@JacksonXmlRootElement(localName = "cpe-item")
public class CpeItem {
@JacksonXmlProperty(localName = "name", isAttribute = true)
private String name;
private String title;
}CpeList.java (关键修改)
package com.dependency.demo;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
import java.util.List; // 导入List接口
@Data
@JacksonXmlRootElement(localName = "cpe-list")
public class CpeList {
// 1. 将字段类型改为 List<CpeItem>
// 2. 使用 @JacksonXmlElementWrapper(useWrapping = false) 明确表示没有列表包装器
// 3. 使用 @JacksonXmlProperty(localName = "cpe-item") 指定列表元素的名称
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "cpe-item")
private List<CpeItem> cpeItems; // 修正后的字段名和类型
}控制器部分的代码相对简单,主要职责是读取XML输入流并使用XmlMapper进行反序列化。这部分代码在解决列表解析问题后无需修改。
XmlController.java
package com.dependency.demo;
import com.fasterxml.jackson.databind.DeserializationFeature; // 可选,用于配置反序列化行为
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.List; // 导入List接口,以便在打印时使用
@RestController
public class XmlController {
@GetMapping("/parse-xml") // 建议为GetMapping添加路径
public CpeList cpeList() throws XMLStreamException, IOException {
InputStream xmlResource = XmlController.class.getClassLoader().getResourceAsStream("test.xml");
// 确保资源文件存在
if (xmlResource == null) {
throw new IOException("XML resource 'test.xml' not found in classpath.");
}
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(xmlResource);
XmlMapper mapper = new XmlMapper();
// 可以在这里配置mapper,例如:
// mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
CpeList cpeList = mapper.readValue(xmlStreamReader, CpeList.class);
// 打印解析结果以验证
System.out.println("Parsed CpeList: " + cpeList);
if (cpeList != null && cpeList.getCpeItems() != null) {
for (CpeItem item : cpeList.getCpeItems()) {
System.out.println("CPE Item - Name: " + item.getName() + ", Title: " + item.getTitle());
}
}
return cpeList;
}
}请确保将上述XML内容保存为src/main/resources/test.xml文件。
当使用修正后的CpeList模型运行上述Spring Boot应用,并访问/parse-xml端点时,Jackson将能够正确解析XML文件中的所有<cpe-item>元素。
示例XML文件 (src/main/resources/test.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<cpe-list>
<cpe-item name="John">
<title>xmlread</title>
</cpe-item>
<cpe-item name="Jack">
<title>testtitle</title>
</cpe-item>
</cpe-list>预期解析结果 (JSON格式的HTTP响应或控制台输出)
{
"cpeItems": [
{
"name": "John",
"title": "xmlread"
},
{
"name": "Jack",
"title": "testtitle"
}
]
}控制台输出:
Parsed CpeList: CpeList(cpeItems=[CpeItem(name=John, title=xmlread), CpeItem(name=Jack, title=testtitle)]) CPE Item - Name: John, Title: xmlread CPE Item - Name: Jack, Title: testtitle
这表明两个cpe-item元素都被成功解析并存储到了cpeItems列表中。
通过遵循这些指导原则和正确使用Jackson的XML注解,开发者可以有效地在Spring Boot应用中处理各种复杂的XML数据结构,实现可靠的数据反序列化。
以上就是使用Jackson在Spring Boot中高效解析XML列表元素的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号