You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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模型存在两个核心问题:

  1. 未对应XML中的<KeyEvents>层级节点,导致结构映射错误
  2. 无法处理动态命名的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 18:34:55