如何将XML转换为java.util.Map,反之亦然

我正在寻找一个轻量级的API(最好是单个类)来转换一个

Map<String,String> map = new HashMap<String,String(); 

到XML,反之亦然,将XML转换回地图。

例:

 Map<String,String> map = new HashMap<String,String(); map.put("name","chris"); map.put("island","faranga"); MagicAPI.toXML(map,"root"); 

结果:

 <root> <name>chris</chris> <island>faranga</island> </root> 

然后回来:

 Map<String,String> map = MagicAPI.fromXML("..."); 

我不想使用JAXB或JSON转换API 。 它不需要照顾嵌套的地图或属性或其他任何东西,只是简单的情况。 有什么build议么?


编辑 :我创build了一个工作副本和粘贴示例。 感谢fvu和Michal Bernhard 。

下载最新的XStream框架 ,'仅核心'就够了。

 Map<String,Object> map = new HashMap<String,Object>(); map.put("name","chris"); map.put("island","faranga"); // convert to XML XStream xStream = new XStream(new DomDriver()); xStream.alias("map", java.util.Map.class); String xml = xStream.toXML(map); // from XML, convert back to map Map<String,Object> map2 = (Map<String,Object>) xStream.fromXML(xml); 

没有转换器或其他东西是必需的。 只要xstream-xyzjar就足够了。

XStream的!

更新 :我在评论中添加了解组部分。

 import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { Map<String,String> map = new HashMap<String,String>(); map.put("name","chris"); map.put("island","faranga"); XStream magicApi = new XStream(); magicApi.registerConverter(new MapEntryConverter()); magicApi.alias("root", Map.class); String xml = magicApi.toXML(map); System.out.println("Result of tweaked XStream toXml()"); System.out.println(xml); Map<String, String> extractedMap = (Map<String, String>) magicApi.fromXML(xml); assert extractedMap.get("name").equals("chris"); assert extractedMap.get("island").equals("faranga"); } public static class MapEntryConverter implements Converter { public boolean canConvert(Class clazz) { return AbstractMap.class.isAssignableFrom(clazz); } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { AbstractMap map = (AbstractMap) value; for (Object obj : map.entrySet()) { Map.Entry entry = (Map.Entry) obj; writer.startNode(entry.getKey().toString()); Object val = entry.getValue(); if ( null != val ) { writer.setValue(val.toString()); } writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Map<String, String> map = new HashMap<String, String>(); while(reader.hasMoreChildren()) { reader.moveDown(); String key = reader.getNodeName(); // nodeName aka element's name String value = reader.getValue(); map.put(key, value); reader.moveUp(); } return map; } } } 

这里包括unmarshall的XStream转换器

 public class MapEntryConverter implements Converter{ public boolean canConvert(Class clazz) { return AbstractMap.class.isAssignableFrom(clazz); } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { AbstractMap<String,String> map = (AbstractMap<String,String>) value; for (Entry<String,String> entry : map.entrySet()) { writer.startNode(entry.getKey().toString()); writer.setValue(entry.getValue().toString()); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Map<String, String> map = new HashMap<String, String>(); while(reader.hasMoreChildren()) { reader.moveDown(); map.put(reader.getNodeName(), reader.getValue()); reader.moveUp(); } return map; } 

XStream怎么样? 不是一类,而是两个瓶子,包括你的许多使用情况,使用起来非常简单但function相当强大。

一个select是推出自己的。 这样做相当简单:

 Document doc = getDocument(); Element root = doc.createElement(rootName); doc.appendChild(root); for (Map.Entry<String,String> element : map.entrySet() ) { Element e = doc.createElement(element.getKey()); e.setTextContent(element.getValue()); root.appendChild(e); } save(doc, file); 

而加载是一个简单的getChildNodes和一个循环。 当然,它有一个XML神的要求,但最多是1个小时的工作锅炉板。

或者你可以看看属性,如果你不太融合XML的格式。

我使用自定义转换器的方法:

 public static class MapEntryConverter implements Converter { public boolean canConvert(Class clazz) { return AbstractMap.class.isAssignableFrom(clazz); } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { AbstractMap map = (AbstractMap) value; for (Object obj : map.entrySet()) { Entry entry = (Entry) obj; writer.startNode(entry.getKey().toString()); context.convertAnother(entry.getValue()); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { // dunno, read manual and do it yourself ;) } } 

但我改变了映射值的序列化,委托给MarshallingContext。 这应该改善解决scheme,以适合复合地图值和嵌套地图。

我写了一段代码,将XML内容转换为地图的多层结构:

 public static Object convertNodesFromXml(String xml) throws Exception { InputStream is = new ByteArrayInputStream(xml.getBytes()); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.parse(is); return createMap(document.getDocumentElement()); } public static Object createMap(Node node) { Map<String, Object> map = new HashMap<String, Object>(); NodeList nodeList = node.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node currentNode = nodeList.item(i); String name = currentNode.getNodeName(); Object value = null; if (currentNode.getNodeType() == Node.ELEMENT_NODE) { value = createMap(currentNode); } else if (currentNode.getNodeType() == Node.TEXT_NODE) { return currentNode.getTextContent(); } if (map.containsKey(name)) { Object os = map.get(name); if (os instanceof List) { ((List<Object>)os).add(value); } else { List<Object> objs = new LinkedList<Object>(); objs.add(os); objs.add(value); map.put(name, objs); } } else { map.put(name, value); } } return map; } 

这个代码转换了这个:

 <house> <door>blue</door> <living-room> <table>wood</table> <chair>wood</chair> </living-room> </house> 

 { "house": { "door": "blue", "living-room": { "table": "wood", "chair": "wood" } } } 

我没有相反的过程,但写起来不是很难。

我发布这个答案不是因为它是你的问题的正确答案,而是因为它是解决同一个问题,而是使用属性来代替。 否则Vikas Gujjar的回答是正确的。

相当数量的数据可能属于属性,但是很难find使用XStream进行操作的任何工作示例,所以下面是一个例子:

示例数据:

 <settings> <property name="prop1" value="foo"/> <property name="prop2" /> <!-- NOTE: The example supports null elements as the backing object is a HashMap. A Properties object would be handled by a PropertiesConverter which wouldn't allow you null values. --> <property name="prop3" value="1"/> </settings> 

MapEntryConverter的实现(稍微重新使用@Vikas Gujjar的实现来代替使用属性):

 public class MapEntryConverter implements Converter { public boolean canConvert(Class clazz) { return AbstractMap.class.isAssignableFrom(clazz); } public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { //noinspection unchecked AbstractMap<String, String> map = (AbstractMap<String, String>) value; for (Map.Entry<String, String> entry : map.entrySet()) { //noinspection RedundantStringToString writer.startNode(entry.getKey().toString()); //noinspection RedundantStringToString writer.setValue(entry.getValue().toString()); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Map<String, String> map = new HashMap<String, String>(); while (reader.hasMoreChildren()) { reader.moveDown(); map.put(reader.getAttribute("name"), reader.getAttribute("value")); reader.moveUp(); } return map; } } 

XStream实例设置,parsing和存储:

  XStream xstream = new XStream(); xstream.autodetectAnnotations(true); xstream.alias("settings", HashMap.class); xstream.registerConverter(new MapEntryConverter()); ... // Parse: YourObject yourObject = (YourObject) xstream.fromXML(is); // Store: xstream.toXML(yourObject); ... 

我发现这在谷歌,但我不想使用XStream,因为它会导致在我的环境中的开销。 我只需要parsing一个文件,因为我没有find任何我喜欢的东西,所以我创build了自己的简单解决scheme来parsing所描述的格式的文件。 所以这里是我的解决scheme:

 public class XmlToMapUtil { public static Map<String, String> parse(InputSource inputSource) throws SAXException, IOException, ParserConfigurationException { final DataCollector handler = new DataCollector(); SAXParserFactory.newInstance().newSAXParser().parse(inputSource, handler); return handler.result; } private static class DataCollector extends DefaultHandler { private final StringBuilder buffer = new StringBuilder(); private final Map<String, String> result = new HashMap<String, String>(); @Override public void endElement(String uri, String localName, String qName) throws SAXException { final String value = buffer.toString().trim(); if (value.length() > 0) { result.put(qName, value); } buffer.setLength(0); } @Override public void characters(char[] ch, int start, int length) throws SAXException { buffer.append(ch, start, length); } } } 

这里有几个TestNG + FEST断言testing:

 public class XmlToMapUtilTest { @Test(dataProvider = "provide_xml_entries") public void parse_returnsMapFromXml(String xml, MapAssert.Entry[] entries) throws Exception { // execution final Map<String, String> actual = XmlToMapUtil.parse(new InputSource(new StringReader(xml))); // evaluation assertThat(actual) .includes(entries) .hasSize(entries.length); } @DataProvider public Object[][] provide_xml_entries() { return new Object[][]{ {"<root />", new MapAssert.Entry[0]}, { "<root><a>aVal</a></root>", new MapAssert.Entry[]{ MapAssert.entry("a", "aVal") }, }, { "<root><a>aVal</a><b>bVal</b></root>", new MapAssert.Entry[]{ MapAssert.entry("a", "aVal"), MapAssert.entry("b", "bVal") }, }, { "<root> \t <a>\taVal </a><b /></root>", new MapAssert.Entry[]{ MapAssert.entry("a", "aVal") }, }, }; } } 

我尝试过不同种类的地图, 转换框工作。 我已经使用了你的地图,并在下面贴上了一些内部贴图。 希望对你有所帮助….

 import java.util.HashMap; import java.util.Map; import cjm.component.cb.map.ToMap; import cjm.component.cb.xml.ToXML; public class Testing { public static void main(String[] args) { try { Map<String, Object> map = new HashMap<String, Object>(); // ORIGINAL MAP map.put("name", "chris"); map.put("island", "faranga"); Map<String, String> mapInner = new HashMap<String, String>(); // SAMPLE INNER MAP mapInner.put("a", "A"); mapInner.put("b", "B"); mapInner.put("c", "C"); map.put("innerMap", mapInner); Map<String, Object> mapRoot = new HashMap<String, Object>(); // ROOT MAP mapRoot.put("ROOT", map); System.out.println("Map: " + mapRoot); System.out.println(); ToXML toXML = new ToXML(); String convertedXML = String.valueOf(toXML.convertToXML(mapRoot, true)); // CONVERTING ROOT MAP TO XML System.out.println("Converted XML: " + convertedXML); System.out.println(); ToMap toMap = new ToMap(); Map<String, Object> convertedMap = toMap.convertToMap(convertedXML); // CONVERTING CONVERTED XML BACK TO MAP System.out.println("Converted Map: " + convertedMap); } catch (Exception e) { e.printStackTrace(); } } } 

输出:

 Map: {ROOT={name=chris, innerMap={b=B, c=C, a=A}, island=faranga}} -------- Map Detected -------- -------- XML created Successfully -------- Converted XML: <ROOT><name>chris</name><innerMap><b>B</b><c>C</c><a>A</a></innerMap><island>faranga</island></ROOT> -------- XML Detected -------- -------- Map created Successfully -------- Converted Map: {ROOT={name=chris, innerMap={b=B, c=C, a=A}, island=faranga}} 

Underscore-lodash库可以将HashMap转换为xml,反之亦然。

代码示例:

 import com.github.underscore.lodash.$; import java.util.*; public class Main { @SuppressWarnings("unchecked") public static void main(String[] args) { Map<String, Object> map = new LinkedHashMap<String, Object>(); map.put("name", "chris"); map.put("island", "faranga"); System.out.println($.toXml(map)); Map<String, Object> newMap = (Map<String, Object>) $.fromXml($.toXml(map)); System.out.println(newMap.get("name")); } } 

现在是2017年,XStream的最新版本需要一个转换器,使其按预期工作。

A转换器支持嵌套地图:

 public class MapEntryConverter implements Converter { @Override public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext marshallingContext) { AbstractMap map = (AbstractMap) value; for (Object obj : map.entrySet()) { Map.Entry entry = (Map.Entry) obj; writer.startNode(entry.getKey().toString()); Object val = entry.getValue(); if (val instanceof Map) { marshal(val, writer, marshallingContext); } else if (null != val) { writer.setValue(val.toString()); } writer.endNode(); } } @Override public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext unmarshallingContext) { Map<String, Object> map = new HashMap<>(); while(reader.hasMoreChildren()) { reader.moveDown(); String key = reader.getNodeName(); // nodeName aka element's name String value = reader.getValue().replaceAll("\\n|\\t", ""); if (StringUtils.isBlank(value)) { map.put(key, unmarshal(reader, unmarshallingContext)); } else { map.put(key, value); } reader.moveUp(); } return map; } @Override public boolean canConvert(Class clazz) { return AbstractMap.class.isAssignableFrom(clazz); } }