如何将Java对象列表编组/解组为无根元素的XML?
问题场景
我有一个结构如下的Employee类Java对象列表:
@XmlRootElement(name = "employee") @XmlAccessorType (XmlAccessType.FIELD) public class Employee { private Integer id; private String firstName; private String lastName; private double income; //Getters and Setters }
我希望将其转换为不含根元素的XML文件,预期输出如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <employee> <id>1</id> <firstName>Lokesh</firstName> <lastName>Gupta</lastName> <income>100.0</income> </employee> <employee> <id>2</id> <firstName>John</firstName> <lastName>Mclane</lastName> <income>200.0</income> </employee>
尝试直接编组列表未成功,用包装类编组会自动添加根元素(包装类代码及输出如下):
@XmlRootElement(name = "employees") @XmlAccessorType (XmlAccessType.FIELD) public class Employees { @XmlElement(name = "employee") private List<Employee> employees = null; }
输出的XML会包含<employees>根节点:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <employees> <employee> <id>1</id> <firstName>Lokesh</firstName> <lastName>Gupta</lastName> <income>100.0</income> </employee> <employee> <id>2</id> <firstName>John</firstName> <lastName>Mclane</lastName> <income>200.0</income> </employee> </employees>
想问:能不能通过JAXB或其他Java转XML库实现需求?同时能否将这种无根元素的XML反向解组为Java POJO?
解决方案
一、使用JAXB实现无根元素编组
JAXB默认要求文档有单一根元素,但可以通过逐个编组列表中的每个对象生成无根节点的XML:
编组代码示例
import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import java.io.FileWriter; import java.util.List; public class JaxbMarshaller { public static void marshalEmployees(List<Employee> employees, String filePath) throws Exception { JAXBContext context = JAXBContext.newInstance(Employee.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_STANDALONE, true); try (FileWriter writer = new FileWriter(filePath)) { // 手动写入XML声明 writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n"); // 逐个编组每个Employee对象 for (Employee emp : employees) { marshaller.marshal(emp, writer); writer.write("\n"); // 换行分隔节点,提升可读性 } } } }
二、反向解组无根元素XML
无根元素的XML不符合标准XML文档结构(要求有且仅有一个根节点),直接用JAXB解组会报错,可通过以下两种方式处理:
1. 手动读取XML节点逐个解组
import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.util.ArrayList; import java.util.List; public class JaxbUnmarshaller { public static List<Employee> unmarshalEmployees(String filePath) throws Exception { List<Employee> employees = new ArrayList<>(); JAXBContext context = JAXBContext.newInstance(Employee.class); Unmarshaller unmarshaller = context.createUnmarshaller(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new File(filePath)); // 提取所有employee节点并逐个解组 NodeList nodeList = doc.getElementsByTagName("employee"); for (int i = 0; i < nodeList.getLength(); i++) { Element element = (Element) nodeList.item(i); Employee emp = (Employee) unmarshaller.unmarshal(element); employees.add(emp); } return employees; } }
2. 借助StAX流读取解组
用StAX遍历XML事件,遇到employee节点时执行解组:
import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; import java.io.FileInputStream; import java.util.ArrayList; import java.util.List; public class StaxUnmarshaller { public static List<Employee> unmarshalEmployees(String filePath) throws Exception { List<Employee> employees = new ArrayList<>(); JAXBContext context = JAXBContext.newInstance(Employee.class); Unmarshaller unmarshaller = context.createUnmarshaller(); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream(filePath)); // 遍历XML事件,解组每个employee节点 while (reader.hasNext()) { if (reader.isStartElement() && "employee".equals(reader.getLocalName())) { Employee emp = unmarshaller.unmarshal(reader, Employee.class).getValue(); employees.add(emp); } reader.next(); } reader.close(); return employees; } }
三、使用Jackson XML库实现(更简洁)
Jackson XML库对无根元素的支持更友好,无需手动处理节点:
1. 添加Maven依赖
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.15.2</version> </dependency>
2. 修改Employee类注解
给Employee类添加Jackson XML的根节点注解(可保留原JAXB注解):
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; @JacksonXmlRootElement(localName = "employee") @XmlRootElement(name = "employee") @XmlAccessorType(XmlAccessType.FIELD) public class Employee { // 字段、Getters和Setters不变 }
3. 编组代码
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import java.io.File; import java.util.List; public class JacksonMarshaller { public static void marshalEmployees(List<Employee> employees, String filePath) throws Exception { XmlMapper xmlMapper = new XmlMapper(); xmlMapper.configure(com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true); xmlMapper.writeValue(new File(filePath), employees); } }
4. 解组代码
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import java.io.File; import java.util.List; public class JacksonUnmarshaller { public static List<Employee> unmarshalEmployees(String filePath) throws Exception { XmlMapper xmlMapper = new XmlMapper(); return xmlMapper.readValue(new File(filePath), new com.fasterxml.jackson.core.type.TypeReference<List<Employee>>(){}); } }
内容的提问来源于stack exchange,提问作者Code Breaker
相关产品推荐
相关产品推荐

