用来记录一些原创性的总结
假设我们需要生成如下这样一段XML代码,condition元素下是若干个包含一个文本节点的元素,这样的元素个数不定。如果需要把它们定义为一个Java类,很明显应该定义为Map结构比较合适。
<request>
<condition>
<key_0>value_0</key_0>
<key_1>value_1</key_1>
<key_2>value_2</key_2>
<key_3>value_3</key_3>
<key_4>value_4</key_4>
</condition>
</request>
为此,我们很容易想到的Java类结构是如下这样的。
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Request {
@XmlElement
private Map<String, String> condition;
}
然后我们来做个测试,看是否可以达到我们想要的效果,测试代码如下:
@Test
public void test() throws Exception {
Request request = new Request();
Map<String, String> condition = new LinkedHashMap<>();
for (int i=0; i<5; i++) {
condition.put("key_" + i, "value_" + i);
}
request.condition = condition;
JAXBContext jaxbContext = JAXBContext.newInstance(Request.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(request, System.out);
}
上面的测试代码输出如下:
<request>
<condition>
<entry>
<key>key_0</key>
<value>value_0</value>
</entry>
<entry>
<key>key_1</key>
<value>value_1</value>
</entry>
<entry>
<key>key_2</key>
<value>value_2</value>
</entry>
<entry>
<key>key_3</key>
<value>value_3</value>
</entry>
<entry>
<key>key_4</key>
<value>value_4</value>
</entry>
</condition>
</request>
没有达到我们想要的效果。不像集合类型那样,JAXB没有对Map进行特殊的处理,它纯粹把它当做一个普通的Java对象来解析的,所以出来的效果就是上面那样的。既然普通的Map会按照Java对象来解析,那么我们如果想要Map结构按照预想的那样转换为XML,那么我们是否可以构造出类似的结构呢?按照这种想法,笔者的思路是把我们的Map包装起来,应用XmlElementWrapper的特殊处理,自定义一个继承自ArrayList的类,把每一个key/value都当做是ArrayList中的一个元素。由于这个元素在展示为XML时需要获取动态的元素名(取key的名称),为此,把它封装为一个JAXBElement对象。为此笔者自己创建的简单的具有类似Map功能的XmlMap结构如下:
public static class XmlMap extends ArrayList<JAXBElement<String>> {
/**
*
*/
private static final long serialVersionUID = -7047805724967522158L;
private Map<String, JAXBElement<String>> keyMap = new HashMap<>();
private static ObjectFactory objectFactory = new ObjectFactory();
public void put(String key, String value) {
if (keyMap.containsKey(key)) {
super.remove(keyMap.get(key));
}
JAXBElement<String> ele = objectFactory.createXmlMap(key, value);
super.add(ele);
keyMap.put(key, ele);
}
}
对应的ObjectFactory类定义如下:
@XmlRegistry
public static class ObjectFactory {
@XmlElementDecl(name="xmlMap")
public JAXBElement<String> createXmlMap(String key, String value) {
QName name = new QName(key);
JAXBElement<String> ele = new JAXBElement<>(name, String.class, value);
return ele;
}
}
ObjectFactory中的createXmlMap把一个key/value对封装为了一个JAXBElement对象,且对应的XML元素名称是取的key的值。然后把我们的Request中的Map替换为我们自定义的XmlMap。
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Request {
@XmlElementWrapper(name="condition")
@XmlElementRef(name="xmlMap")
private XmlMap condition;
}
测试类里面也只是把Map替换为XmlMap。
@Test
public void test() throws Exception {
Request request = new Request();
XmlMap condition = new XmlMap();
for (int i=0; i<5; i++) {
condition.put("key_" + i, "value_" + i);
}
request.condition = condition;
JAXBContext jaxbContext = JAXBContext.newInstance(Request.class, ObjectFactory.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(request, System.out);
}
然后运行上面的测试用例,出来的效果如下:
<request>
<condition>
<key_0>value_0</key_0>
<key_1>value_1</key_1>
<key_2>value_2</key_2>
<key_3>value_3</key_3>
<key_4>value_4</key_4>
</condition>
</request>
跟我们一开始期望的一样。笔者示例中的XmlMap只有简单的类似于java.util.Map的存储单个元素的功能,如需要更多的类似Map的功能,则可以做进一步的封装。
接下来,在下一篇博文中笔者还会介绍另一种把Map转换为XML的方法,其在Request中定义的将是一个真正的java.util.Map对象。
(注:本文由Elim写于2017年8月9日)