前言:
JAXB(Java Architecture for XML Binding) 用于 XML 绑定的 Java 体系结构(JAXB)是允许 Java 开发人员将 Java 类映射到 XML 表示形式的软件框架。 JAXB 支持将 Java 对象编组为 XML,然后将 XML 解组为 Java 对象。
如下:
package com.c.utils.jaxb;
import com.c.utils.LoggerUtils;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
/**
* @author zhy
*/
public class JAXbUtils {
private JAXbUtils() {
}
public static <T> void save(T obj, File file) {
try {
JAXBContext jc = JAXBContext.newInstance(obj.getClass());
Marshaller jaxbMarshaller = jc.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(obj, file);
// jaxbMarshaller.marshal(obj, System.out);
} catch (JAXBException e) {
LoggerUtils.getLogger(JAXbUtils.class).error(e.getMessage(), e);
}
}
public static <T> T load(File file, Class<? extends T> type) {
try {
JAXBContext jc = JAXBContext.newInstance(type);
Unmarshaller unmarshaller = jc.createUnmarshaller();
return (T) unmarshaller.unmarshal(file);
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
}
JAXB 与 Map<String,String>
JAXB 默认转换 map 的结果
JABX 默认将 Map<String,String> 序列化成:
<item>
<key>xxx<key>
<value>xxx<value>
</item>
如下:
package com.c.utils.jaxb;
import org.junit.Assert;
import org.junit.Test;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class JAXbUtilsTest {
@Test
public void saveMap() {
Config config = new Config();
config.map = new HashMap<>();
String naturalSelection = "自然选择号";
String blueSpace = "蓝色空间号";
config.map.put(naturalSelection, "东方延绪");
config.map.put(blueSpace, "褚岩");
File file = new File("target/config.xml");
JAXbUtils.save(config, file);
Config newConfig = JAXbUtils.load(file, Config.class);
Assert.assertNotNull(newConfig);
Assert.assertEquals(config.map.get(naturalSelection), newConfig.map.get(naturalSelection));
Assert.assertEquals(config.map.get(blueSpace), newConfig.map.get(blueSpace));
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
static class Config {
Map<String, String> map = new HashMap<>();
}
}
target/config.xml 文件内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<map>
<entry>
<key>自然选择号</key>
<value>东方延绪</value>
</entry>
<entry>
<key>蓝色空间号</key>
<value>褚岩</value>
</entry>
</map>
</config>
优化 JAXB 转换 MAP
理想状态,可以将其简化为以下形式( 标签名为key,内容为 value ):
<key>value</key>
则需要自定义转换器,将map转换为 w3c 的 Element 对象(其他自定义类型也可以这样实现),实现如下:
package com.c.utils.jaxb;
import com.c.utils.LoggerUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.util.HashMap;
import java.util.Map;
public class MapAdapter extends XmlAdapter<Object, Map<String, String>> {
public static final String TEXT = "#text";
@Override
public Map<String, String> unmarshal(Object v) {
Map<String, String> map = new HashMap<>();
if (!(v instanceof Element)) {
return map;
}
NodeList childNodes = ((Element) v).getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (TEXT.equals(item.getNodeName())) {
continue;
}
map.put(item.getNodeName(), item.getTextContent());
}
return map;
}
@Override
public Object marshal(Map<String, String> v) {
Document document = null;
try {
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
} catch (ParserConfigurationException e) {
LoggerUtils.getLogger(getClass()).error(e.getMessage(), e);
return null;
}
Element rootElement = document.createElement("map");
document.appendChild(rootElement);
for (Map.Entry<String, String> entry : v.entrySet()) {
Element element = document.createElement(entry.getKey());
element.setTextContent(entry.getValue());
rootElement.appendChild(element);
}
return rootElement;
}
}
然后在需要序列化的 map 字段上加上 @XmlJavaTypeAdapter(MapAdapter.class) 注解。
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
static class Config {
@XmlElement(name = "properties")
@XmlJavaTypeAdapter(MapAdapter.class)
Map<String, String> map = new HashMap<>();
}
修改后 target/config.xml 文件内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<properties>
<自然选择号>东方延绪</自然选择号>
<蓝色空间号>褚岩</蓝色空间号>
</properties>
</config>