用来记录一些原创性的总结
之前介绍过两种Map转XML的方式,接下来将介绍两种XML转Map的方式,这两种方式都可以兼容Map转XML,区别在于一种是针对于Map在XML中有根节点,一种是没有根节点的。本文将介绍有根节点的场景。
考虑下面这样一段XML,root根节点下有other节点和map节点,这两个是固定的,但是map节点下的节点的数量和名称是不固定的,形式都是<key>value</key>
式的。
<root>
<other>123</other>
<map>
<key1>value1</key1>
<key2>value2</key2>
<key3></key3>
<key4 />
</map>
</root>
很明显,这样的XML映射为Java中的Map比较合适,为此定义了与它匹配的Java类的结构如下:
@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="root")
public static class RootObj {
private String other;
private Map<String, String> map;
}
很不幸的是JAXB不支持直接这样映射,它能支持的是把map节点下的动态节点用XmlAnyElement进行映射,而map节点可以映射为一个对象,为此建立了一个能够接收map节点及其以下字节点类来作为中转,其结构如下:
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public static class MapHolder {
@XmlAnyElement
private List<Element> entries;
}
刚刚说了MapHolder是用于作为中转的,而我们真正最终要映射的是Map<String, String>,为此建立了一个对应的适配器。使用JAXB的XmlAdapter机制,其实现如下。其中unmarshal用于XML转Map,marshal用于Map转XML。
public static class XmlToMapAdapter extends XmlAdapter<MapHolder, Map<String, String>> {
@Override
public Map<String, String> unmarshal(MapHolder v) throws Exception {
if (v == null) {
return null;
}
Map<String, String> map = new HashMap<>();
List<Element> entries = v.getEntries();
if (entries != null && !entries.isEmpty()) {
entries.forEach(ele -> {
String key = ele.getLocalName();
String value = ele.getTextContent();
map.put(key, value);
});
}
return map;
}
@Override
public MapHolder marshal(Map<String, String> v) throws Exception {
if (v == null) {
return null;
}
MapHolder holder = new MapHolder();
if (!v.isEmpty()) {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
List<Element> entries = new ArrayList<>();
v.forEach((key, value) -> {
Element ele = doc.createElement(key);
ele.setTextContent(value);
entries.add(ele);
});
holder.setEntries(entries);
}
return holder;
}
}
定义了Map到MapHolder的适配器XmlToMapAdapter后,我们需要告诉JAXB,为此我们在RootObj的map属性上加上@XmlJavaTypeAdapter指定使用的XmlAdapter是XmlToMapAdapter,代码如下:
@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="root")
public static class RootObj {
private String other;
@XmlJavaTypeAdapter(XmlToMapAdapter.class)
private Map<String, String> map;
}
至此,我们Map结构的XML就能够顺利的转换为Map了,而Map也可以顺利的转换为XML格式的Map了。进行单元测试如下:
@Test
public void test() throws Exception {
String xml = "<root><other>123</other><map><key1>value1</key1><key2>value2</key2><key3></key3><key4/></map></root>";
RootObj rootObj = this.unmarshal(xml);
JAXBContext jaxbContext = JAXBContext.newInstance(RootObj.class);
Marshaller marshaller = jaxbContext.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(rootObj, writer);
this.unmarshal(writer.toString());//验证marshal的结果是否正确
}
private RootObj unmarshal(String xml) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(RootObj.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
RootObj rootObj = (RootObj) unmarshaller.unmarshal(new StringReader(xml));
Assert.assertNotNull(rootObj.getMap());
Assert.assertEquals("123", rootObj.getOther());
Assert.assertEquals(4, rootObj.getMap().size());
Assert.assertEquals("value1", rootObj.getMap().get("key1"));
Assert.assertEquals("value2", rootObj.getMap().get("key2"));
Assert.assertEquals("", rootObj.getMap().get("key3"));//key3的值是空字符串
Assert.assertEquals("", rootObj.getMap().get("key4"));//key4的值是空字符串
return rootObj;
}
完整代码如下:
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.parsers.DocumentBuilderFactory;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import lombok.Data;
/**
* 这种介绍的是用一个元素包裹起来的Map,即Map是有根节点的
* @author Elim
*/
public class XmlToMapTest {
@Test
public void test() throws Exception {
String xml = "<root><other>123</other><map><key1>value1</key1><key2>value2</key2><key3></key3><key4/></map></root>";
RootObj rootObj = this.unmarshal(xml);
JAXBContext jaxbContext = JAXBContext.newInstance(RootObj.class);
Marshaller marshaller = jaxbContext.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(rootObj, writer);
this.unmarshal(writer.toString());//验证marshal的结果是否正确
}
private RootObj unmarshal(String xml) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(RootObj.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
RootObj rootObj = (RootObj) unmarshaller.unmarshal(new StringReader(xml));
Assert.assertNotNull(rootObj.getMap());
Assert.assertEquals("123", rootObj.getOther());
Assert.assertEquals(4, rootObj.getMap().size());
Assert.assertEquals("value1", rootObj.getMap().get("key1"));
Assert.assertEquals("value2", rootObj.getMap().get("key2"));
Assert.assertEquals("", rootObj.getMap().get("key3"));//key3的值是空字符串
Assert.assertEquals("", rootObj.getMap().get("key4"));//key4的值是空字符串
return rootObj;
}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="root")
public static class RootObj {
private String other;
@XmlJavaTypeAdapter(XmlToMapAdapter.class)
private Map<String, String> map;
}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public static class MapHolder {
@XmlAnyElement
private List<Element> entries;
}
public static class XmlToMapAdapter extends XmlAdapter<MapHolder, Map<String, String>> {
@Override
public Map<String, String> unmarshal(MapHolder v) throws Exception {
if (v == null) {
return null;
}
Map<String, String> map = new HashMap<>();
List<Element> entries = v.getEntries();
if (entries != null && !entries.isEmpty()) {
entries.forEach(ele -> {
String key = ele.getLocalName();
String value = ele.getTextContent();
map.put(key, value);
});
}
return map;
}
@Override
public MapHolder marshal(Map<String, String> v) throws Exception {
if (v == null) {
return null;
}
MapHolder holder = new MapHolder();
if (!v.isEmpty()) {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
List<Element> entries = new ArrayList<>();
v.forEach((key, value) -> {
Element ele = doc.createElement(key);
ele.setTextContent(value);
entries.add(ele);
});
holder.setEntries(entries);
}
return holder;
}
}
}
注:本文是由Elim所写的JAXB系列中的一篇,如果单纯看这篇看不懂的,请按照博客系列顺序进行查看,以掌握必要的基础知识。