JAXB Unmarshalling如何处理不同Field包装元素下的相同User节点
方案1:使用@XmlAnyElement接收动态Field节点(实现简单)
该方案无需修改XML结构,也无需提前知道Field节点的数量和命名规则,通过JAXB自带的任意元素注解接收所有未知外层节点,再统一提取内部的User元素。
步骤1:定义User实体类
import javax.xml.bind.annotation.*; @XmlRootElement(name = "User") @XmlAccessorType(XmlAccessType.FIELD) public class User { @XmlElement(name = "Name") private String name; @XmlElement(name = "Id") private Integer id; // 自行补充getter、setter方法 }
步骤2:定义Response根节点类
import org.w3c.dom.Element; import javax.xml.bind.*; import javax.xml.bind.annotation.*; import java.util.ArrayList; import java.util.List; @XmlRootElement(name = "Response") @XmlAccessorType(XmlAccessType.FIELD) public class Response { // 接收所有未在类中显式定义的子节点 @XmlAnyElement(lax = true) private List<Object> dynamicFieldNodes; // 直接返回所有提取到的User对象 public List<User> getAllUsers() throws JAXBException { List<User> users = new ArrayList<>(); Unmarshaller userUnmarshaller = JAXBContext.newInstance(User.class).createUnmarshaller(); for (Object node : dynamicFieldNodes) { if (node instanceof Element) { var userNodes = ((Element) node).getElementsByTagName("User"); for (int i = 0; i < userNodes.getLength(); i++) { users.add((User) userUnmarshaller.unmarshal(userNodes.item(i))); } } } return users; } }
步骤3:执行反序列化
JAXBContext context = JAXBContext.newInstance(Response.class); Unmarshaller unmarshaller = context.createUnmarshaller(); Response response = (Response) unmarshaller.unmarshal(你的XML输入源); List<User> userList = response.getAllUsers();
方案2:预处理XML事件流剥离Field层(性能更高,适合大体积XML)
该方案通过StAX事件流在反序列化前直接过滤掉所有Field节点的起止标签,相当于把所有User节点直接作为Response的子节点,无需二次反序列化User元素。
步骤1:简化Response实体类定义
import javax.xml.bind.annotation.*; import java.util.List; @XmlRootElement(name = "Response") @XmlAccessorType(XmlAccessType.FIELD) public class Response { @XmlElement(name = "User") private List<User> users; // 自行补充getter、setter方法 }
步骤2:使用过滤后的事件流执行反序列化
import javax.xml.bind.*; import javax.xml.stream.*; import javax.xml.stream.events.XMLEvent; import java.io.FileReader; // 构造原始XML事件读取器 XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); XMLEventReader originalReader = xmlInputFactory.createXMLEventReader(new FileReader("你的XML文件路径")); // 自定义过滤读取器,跳过所有Field节点的起止标签 XMLEventReader filteredReader = new EventReaderDelegate(originalReader) { @Override public XMLEvent nextEvent() throws XMLStreamException { XMLEvent event = super.nextEvent(); String nodeName = ""; if (event.isStartElement()) { nodeName = event.asStartElement().getName().getLocalPart(); } else if (event.isEndElement()) { nodeName = event.asEndElement().getName().getLocalPart(); } // 只要是Field开头的节点就跳过 if (nodeName.startsWith("Field")) { return nextEvent(); } return event; } }; // 直接用过滤后的事件流反序列化 JAXBContext context = JAXBContext.newInstance(Response.class); Unmarshaller unmarshaller = context.createUnmarshaller(); Response response = (Response) unmarshaller.unmarshal(filteredReader); List<User> userList = response.getUsers();
注意事项
如果使用Jakarta EE 9+版本的JAXB(Jakarta XML Binding),只需要把所有javax.xml.bind的包引用替换为jakarta.xml.bind即可,逻辑完全一致。
内容的提问来源于stack exchange,提问作者3KTech
相关产品推荐
相关产品推荐

