如何强制JAXB编组器遵循XmlElementRefs注解中元素的顺序?
解决JAXB编组时按@XmlElementRefs定义顺序输出元素的问题
要实现不管列表添加顺序,XML严格按@XmlElementRefs里的元素类型顺序输出,核心是在编组前手动对列表按注解定义的顺序排序,以下是具体实现步骤:
1. 解析@XmlElementRefs的元素顺序映射
先写一个工具方法,解析目标类上的@XmlElementRefs注解,把每个元素类型和它在注解中的位置索引对应起来,为后续排序提供依据:
import jakarta.xml.bind.annotation.XmlElementRef; import jakarta.xml.bind.annotation.XmlElementRefs; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class JaxbElementOrderUtils { public static <T> Map<Class<?>, Integer> getElementOrderMap(Class<T> clazz, String listFieldName) throws NoSuchFieldException { Map<Class<?>, Integer> orderMap = new HashMap<>(); Field field = clazz.getDeclaredField(listFieldName); XmlElementRefs refs = field.getAnnotation(XmlElementRefs.class); if (refs != null) { XmlElementRef[] elements = refs.value(); for (int i = 0; i < elements.length; i++) { orderMap.put(elements[i].type(), i); } } return orderMap; } }
2. 编写列表排序器
基于上面的顺序映射,实现Comparator来排序泛型列表中的元素:
import java.util.Comparator; import java.util.Map; public class JaxbElementComparator implements Comparator<Object> { private final Map<Class<?>, Integer> elementOrderMap; public JaxbElementComparator(Map<Class<?>, Integer> elementOrderMap) { this.elementOrderMap = elementOrderMap; } @Override public int compare(Object o1, Object o2) { int index1 = elementOrderMap.getOrDefault(o1.getClass(), Integer.MAX_VALUE); int index2 = elementOrderMap.getOrDefault(o2.getClass(), Integer.MAX_VALUE); return Integer.compare(index1, index2); } }
3. 结合Spring Jaxb2Marshaller使用
因为你用的是Spring的Jaxb2Marshaller,可以在编组前手动对列表排序,以下是示例(假设你的实体类是MyEntity,列表属性名为items):
import org.springframework.oxm.jaxb.Jaxb2Marshaller; import javax.xml.transform.stream.StreamResult; import java.io.StringWriter; import java.util.Collections; import jakarta.xml.bind.Marshaller; // 初始化实体并添加元素(顺序随机) MyEntity entity = new MyEntity(); entity.getItems().add(new ThirdElement()); entity.getItems().add(new FirstElement()); entity.getItems().add(new SecondElement()); // 获取元素顺序映射 Map<Class<?>, Integer> orderMap = JaxbElementOrderUtils.getElementOrderMap(MyEntity.class, "items"); // 对列表按注解定义顺序排序 Collections.sort(entity.getItems(), new JaxbElementComparator(orderMap)); // 配置并执行编组 Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setClassesToBeBound(MyEntity.class); marshaller.setMarshallerProperties(Map.of(Marshaller.JAXB_FRAGMENT, true)); StringWriter writer = new StringWriter(); marshaller.marshal(entity, new StreamResult(writer)); String xmlOutput = writer.toString();
注意事项
- 如果列表中存在@XmlElementRefs未定义的类型,上面的逻辑会把它们放到最后,你可以根据需求修改
getOrDefault的默认值,比如改成抛出IllegalArgumentException来校验非法元素。 JAXB_FRAGMENT=true仅控制是否生成根XML元素,不影响列表元素的排序逻辑,两者完全兼容。
内容的提问来源于stack exchange,提问作者Antonio
相关产品推荐
相关产品推荐

