如何使用JAXB处理含动态event_[x]元素的XML文件?
动态命名event元素的JAXB解析解决方案
问题背景
给定如下XML结构,其中event_*元素的数量不固定(从0到数百个都有可能),需要用JAXB解析所有event_*中的distance值并打印,同时保留Summary的解析:
<?xml version="1.0" encoding="UTF-8"?> <test> <KeyEvents> <num_events>4</num_events> <event_1> <distance>0.044</distance> </event_1> <event_2> <distance>0.055</distance> </event_2> <event_3> <distance>3.778</distance> </event_3> <event_4> <distance>3.822</distance> </event_4> <Summary> <total_distance>7.699</total_distance> </Summary> </KeyEvents> </test>
原JAXB模型存在两个核心问题:
- 未对应XML中的
<KeyEvents>层级节点,导致结构映射错误 - 无法处理动态命名的
event_*元素,直接用Map存储也没有配置正确的解析规则
解决方案一:用List存储所有Event(推荐,适合遍历需求)
1. 修正模型结构
先补充KeyEvents类对应XML的层级,再用@XmlAnyElement配合工厂类捕获动态event_*元素:
Test类(根节点)
@XmlRootElement(name = "test") @XmlAccessorType(XmlAccessType.FIELD) public class Test { @XmlElement(name = "KeyEvents") private KeyEvents keyEvents; // Getter & Setter public KeyEvents getKeyEvents() { return keyEvents; } public void setKeyEvents(KeyEvents keyEvents) { this.keyEvents = keyEvents; } }
KeyEvents类(对应节点)
@XmlAccessorType(XmlAccessType.FIELD) public class KeyEvents { @XmlElement(name = "num_events") private int numEvents; // 用List存储所有event,通过工厂类解析动态命名元素 @XmlAnyElement(factory = EventElementFactory.class) private List<Event> events; @XmlElement(name = "Summary") private Summary summary; // Getters & Setters public int getNumEvents() { return numEvents; } public void setNumEvents(int numEvents) { this.numEvents = numEvents; } public List<Event> getEvents() { return events; } public void setEvents(List<Event> events) { this.events = events; } public Summary getSummary() { return summary; } public void setSummary(Summary summary) { this.summary = summary; } }
Event类(对应单个event_*节点)
@XmlAccessorType(XmlAccessType.FIELD) public class Event { @XmlElement(name = "distance") private double distance; // Getter & Setter public double getDistance() { return distance; } public void setDistance(double distance) { this.distance = distance; } }
Summary类(对应节点)
@XmlAccessorType(XmlAccessType.FIELD) public class Summary { @XmlElement(name = "total_distance") private double totalDistance; // Getter & Setter public double getTotalDistance() { return totalDistance; } public void setTotalDistance(double totalDistance) { this.totalDistance = totalDistance; } }
2. 编写元素解析工厂类
这个类负责把DOM中的event_*元素转换成Event对象:
import org.w3c.dom.Element; import javax.xml.bind.annotation.XmlAnyElement; public class EventElementFactory { @XmlAnyElement public static Event createEvent(Element element) { // 只处理以"event_"开头的元素 if (element.getTagName().startsWith("event_")) { Event event = new Event(); // 提取distance值 String distanceStr = element.getElementsByTagName("distance").item(0).getTextContent(); event.setDistance(Double.parseDouble(distanceStr)); return event; } // 非event_*元素返回null,交给其他解析逻辑(比如Summary) return null; } }
3. 解析并遍历打印
import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import java.io.File; public class XmlParser { public static void main(String[] args) throws Exception { JAXBContext context = JAXBContext.newInstance(Test.class); Unmarshaller unmarshaller = context.createUnmarshaller(); // 替换为你的XML文件路径 Test test = (Test) unmarshaller.unmarshal(new File("test.xml")); KeyEvents keyEvents = test.getKeyEvents(); // 遍历所有event的distance if (keyEvents.getEvents() != null) { keyEvents.getEvents().forEach(event -> System.out.println("Distance: " + event.getDistance()) ); } // 打印总距离 System.out.println("Total Distance: " + keyEvents.getSummary().getTotalDistance()); } }
解决方案二:用Map存储Event(保留原始元素名)
如果需要保留event_1、event_2这类原始元素名称,可以用@XmlJavaTypeAdapter实现Map存储:
修改KeyEvents类
@XmlAccessorType(XmlAccessType.FIELD) public class KeyEvents { @XmlElement(name = "num_events") private int numEvents; @XmlJavaTypeAdapter(EventMapAdapter.class) private Map<String, Event> events; @XmlElement(name = "Summary") private Summary summary; // Getters & Setters 同上 }
编写Map适配器
import javax.xml.bind.annotation.adapters.XmlAdapter; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.util.HashMap; import java.util.Map; public class EventMapAdapter extends XmlAdapter<Object, Map<String, Event>> { @Override public Map<String, Event> unmarshal(Object obj) throws Exception { Map<String, Event> eventMap = new HashMap<>(); Element parentElement = (Element) obj; NodeList nodeList = parentElement.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { if (nodeList.item(i) instanceof Element) { Element element = (Element) nodeList.item(i); String tagName = element.getTagName(); if (tagName.startsWith("event_")) { Event event = new Event(); String distanceStr = element.getElementsByTagName("distance").item(0).getTextContent(); event.setDistance(Double.parseDouble(distanceStr)); eventMap.put(tagName, event); } } } return eventMap.isEmpty() ? null : eventMap; } // 生成XML时需要实现marshal方法,仅解析的话可以返回空元素 @Override public Object marshal(Map<String, Event> map) throws Exception { return null; } }
遍历Map打印
if (keyEvents.getEvents() != null) { keyEvents.getEvents().forEach((key, event) -> System.out.println(key + " distance: " + event.getDistance()) ); }
内容的提问来源于stack exchange,提问作者Jardo
相关产品推荐
相关产品推荐

