如何将带不同key的XML<supplement>映射到Java POJO字段或HashMap?
XML到Java POJO的映射方案
XML 文件内容
<foobar name="quux"> <supplement key="head">this is a text row</supplement> <supplement key="body">this is some other text row</supplement> </foobar>
目标Java POJO结构
希望将<supplement>标签按key属性分别映射到POJO的head和body字段:
@XmlRootElement(name = "foobar") @XmlAccessorType(XmlAccessType.FIELD) public class Foobar { @XmlAttribute private String name; private String head; // 对应XML中key="head"的supplement标签内容 private String body; // 对应XML中key="body"的supplement标签内容 }
问题说明
能否实现上述直接映射?若可以该如何操作?
另外尝试将所有<supplement>标签存入HashMap<String, String>(以key属性值为键)但未成功,目前临时方案是映射为列表:
@XmlElement(name = "supplement") private List<NewSupplement> supplements = new ArrayList<>();
但该方案依赖XML中标签的顺序,容易引发问题,因此更倾向于直接映射到单个字段或HashMap方案,求可行建议。
解决方案
方案一:直接映射到单个字段
可以通过EclipseLink MOXy(JAXB的扩展库,标准JAXB不支持@XmlPath)提供的@XmlPath注解实现按属性筛选的映射,步骤如下:
- 引入MOXy依赖(Maven为例):
<dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.moxy</artifactId> <version>4.0.2</version> </dependency>
- 修改POJO,添加
@XmlPath注解:
@XmlRootElement(name = "foobar") @XmlAccessorType(XmlAccessType.FIELD) public class Foobar { @XmlAttribute private String name; // 匹配key="head"的supplement标签文本内容 @XmlPath("supplement[@key='head']/text()") private String head; // 匹配key="body"的supplement标签文本内容 @XmlPath("supplement[@key='body']/text()") private String body; // 生成getter、setter方法 // ... }
- 配置JAXB使用MOXy:
在POJO所在包下创建jaxb.properties文件,内容为:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
此方案可直接将指定key的<supplement>内容映射到对应字段,完全不受标签顺序影响。
方案二:映射到HashMap
基于标准JAXB实现,无需额外依赖,通过自定义XmlAdapter将<supplement>集合转换为HashMap:
- 定义单个
<supplement>的辅助类:
public class Supplement { @XmlAttribute private String key; @XmlValue private String value; // 生成getter、setter方法 // ... }
- 编写自定义
XmlAdapter:
public class SupplementMapAdapter extends XmlAdapter<List<Supplement>, Map<String, String>> { @Override public Map<String, String> unmarshal(List<Supplement> supplements) throws Exception { Map<String, String> map = new HashMap<>(); for (Supplement s : supplements) { map.put(s.getKey(), s.getValue()); } return map; } @Override public List<Supplement> marshal(Map<String, String> map) throws Exception { List<Supplement> supplements = new ArrayList<>(); for (Map.Entry<String, String> entry : map.entrySet()) { Supplement s = new Supplement(); s.setKey(entry.getKey()); s.setValue(entry.getValue()); supplements.add(s); } return supplements; } }
- 修改Foobar类使用适配器:
@XmlRootElement(name = "foobar") @XmlAccessorType(XmlAccessType.FIELD) public class Foobar { @XmlAttribute private String name; @XmlElement(name = "supplement") @XmlJavaTypeAdapter(SupplementMapAdapter.class) private Map<String, String> supplementMap; // 生成getter、setter方法 // 可添加便捷方法快速获取指定key的值 public String getHead() { return supplementMap != null ? supplementMap.get("head") : null; } public String getBody() { return supplementMap != null ? supplementMap.get("body") : null; } }
此方案将所有<supplement>按key存入HashMap,不受标签顺序影响,还可通过自定义方法快速获取目标字段值。
内容的提问来源于stack exchange,提问作者Peter Odd
相关产品推荐
相关产品推荐

