Wie man XML zu java.util.Map konvertiert und umgekehrt

Ich suche eine Lightweight-API (vorzugsweise eine einzelne class), um ein

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

zu XML und, umgekehrt, wandeln Sie das XML zurück zu einer Karte um.

Beispiel:

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

Ergebnis:

  chris faranga  

und zurück:

 Map map = MagicAPI.fromXML("..."); 

Ich möchte JAXB oder JSON-Konvertierungs-API nicht verwenden . Es muss sich nicht um verschachtelte Maps oder Attribute oder irgendetwas anderes kümmern, nur diesen einfachen Fall. Irgendwelche Vorschläge?


Bearbeiten : Ich erstellte eine Arbeitskopie und Paste Probe. Danke an fvu und Michal Bernhard .

Laden Sie das neueste XStream-Framework herunter , “nur Core” ist ausreichend.

 Map map = new HashMap(); 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 map2 = (Map) xStream.fromXML(xml); 

Es sind keine Konverter oder irgendetwas anderes erforderlich. Nur der xstream-xyzjar ist genug.

XStream!

Aktualisiert : Ich habe unmarshal Teil hinzugefügt, wie in den Kommentaren angefordert ..

 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 map = new HashMap(); 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 extractedMap = (Map) 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 map = new HashMap(); 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; } } } 

Hier ist der Konverter für XStream inkludiert

 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 map = (AbstractMap) value; for (Entry entry : map.entrySet()) { writer.startNode(entry.getKey().toString()); writer.setValue(entry.getValue().toString()); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Map map = new HashMap(); while(reader.hasMoreChildren()) { reader.moveDown(); map.put(reader.getNodeName(), reader.getValue()); reader.moveUp(); } return map; } 

Wie wäre es mit XStream ? Nicht 1 class, sondern 2 Gläser für viele Anwendungsfälle einschließlich Ihrer, sehr einfach zu bedienen, aber ziemlich mächtig.

Eine Möglichkeit wäre, Ihre eigenen zu rollen. Es wäre ziemlich einfach zu tun:

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

und die Last ist eine ebenso einfache getChildNodes und eine Schleife. Sicher, es hat ein bisschen Kesselplatte, die die XML-Götter verlangen, aber es ist höchstens 1 Stunden Arbeit.

Oder Sie können sich Properties ansehen, wenn Sie nicht zu sehr mit dem XML-Format vertraut sind.

Ich habe den Ansatz mit dem benutzerdefinierten Konverter verwendet:

 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 ;) } } 

Aber ich habe die Serialisierung des Kartenwerts geändert, um sie an den MarshallingContext zu delegieren. Dies sollte die Lösung für das Arbeiten mit zusammengesetzten Kartenwerten und verschachtelten Karten verbessern.

Ich habe einen Code geschrieben, der einen XML-Inhalt in eine mehrschichtige Kartenstruktur umwandelt:

 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 map = new HashMap(); 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)os).add(value); } else { List objs = new LinkedList(); objs.add(os); objs.add(value); map.put(name, objs); } } else { map.put(name, value); } } return map; } 

Dieser Code wandelt das um:

  blue  wood
wood

in

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

Ich habe nicht den umgekehrten process, aber das muss nicht sehr schwer zu schreiben sein.

Ich poste dies als Antwort, nicht weil es die richtige Antwort auf Ihre Frage ist, sondern weil es eine Lösung für dasselbe Problem ist, aber stattdessen Attribute verwendet. Ansonsten ist Vikas Gujjars Antwort richtig.

Ziemlich oft könnten Ihre Daten in Attributen liegen, aber es ist ziemlich schwierig, irgendwelche Arbeitsbeispiele zu finden, die XStream verwenden, also hier ist eins:

Beispieldaten:

       

Implementierung von MapEntryConverter (die Implementierung von @Vikas Gujjar wurde etwas überarbeitet, um stattdessen Attribute zu verwenden):

 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 map = (AbstractMap) value; for (Map.Entry 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 map = new HashMap(); while (reader.hasMoreChildren()) { reader.moveDown(); map.put(reader.getAttribute("name"), reader.getAttribute("value")); reader.moveUp(); } return map; } } 

XStream-Instanz einrichten, analysieren und speichern:

  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); ... 

Ich habe das bei Google gefunden, aber ich möchte XStream nicht verwenden, weil es in meiner Umgebung zu viel Aufwand verursacht. Ich musste nur eine Datei analysieren, und da ich nichts gefunden habe, was ich mag, habe ich meine eigene einfache Lösung zum Parsen einer Datei des von Ihnen beschriebenen Formats erstellt. Also hier ist meine Lösung:

 public class XmlToMapUtil { public static Map 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 result = new HashMap(); @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); } } } 

Hier sind ein paar TestNG + FEST Assert Tests:

 public class XmlToMapUtilTest { @Test(dataProvider = "provide_xml_entries") public void parse_returnsMapFromXml(String xml, MapAssert.Entry[] entries) throws Exception { // execution final Map 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[][]{ {"", new MapAssert.Entry[0]}, { "aVal", new MapAssert.Entry[]{ MapAssert.entry("a", "aVal") }, }, { "aValbVal", new MapAssert.Entry[]{ MapAssert.entry("a", "aVal"), MapAssert.entry("b", "bVal") }, }, { " \t \taVal ", new MapAssert.Entry[]{ MapAssert.entry("a", "aVal") }, }, }; } } 

Ich habe verschiedene Arten von Karten ausprobiert und die Conversion Box funktioniert. Ich habe Ihre Karte verwendet und ein Beispiel mit einigen inneren Karten eingefügt. Hoffe es ist hilfreich für dich ….

 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 map = new HashMap(); // ORIGINAL MAP map.put("name", "chris"); map.put("island", "faranga"); Map mapInner = new HashMap(); // SAMPLE INNER MAP mapInner.put("a", "A"); mapInner.put("b", "B"); mapInner.put("c", "C"); map.put("innerMap", mapInner); Map mapRoot = new HashMap(); // 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 convertedMap = toMap.convertToMap(convertedXML); // CONVERTING CONVERTED XML BACK TO MAP System.out.println("Converted Map: " + convertedMap); } catch (Exception e) { e.printStackTrace(); } } } 

Ausgabe:

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

Underscore-Java- Bibliothek kann Map in XML konvertieren. Live-Beispiel

Codebeispiel:

 import com.github.underscore.lodash.U; import java.util.*; public class Main { public static void main(String[] args) { Map map = new LinkedHashMap(); map.put("name", "chris"); map.put("island", "faranga"); System.out.println(U.toXml(map)); //  // chris // faranga //  } } 

Jetzt ist es 2017, die neueste Version von XStream erfordert einen Konverter, damit es wie erwartet funktioniert.

Ein Konverter unterstützt verschachtelte Karten:

 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 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); } } 

In meinem Fall konvertiere ich DBresponse in Camel ctx nach XML. JDBC-Executor gibt die ArrayList (Zeilen) mit LinkedCaseInsensitiveMap (einzelne Zeile) zurück. Aufgabe – Erstellen eines XML-Objekts basierend auf DBResponce.

 import java.io.StringWriter; import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.springframework.util.LinkedCaseInsensitiveMap; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; public class ConvertDBToXMLProcessor implements Processor { public void process(List body) { if (body instanceof ArrayList) { ArrayList rows = (ArrayList) body; DocumentBuilder builder = null; builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document document = builder.newDocument(); Element rootElement = document.createElement("DBResultSet"); for (LinkedCaseInsensitiveMap row : rows) { Element newNode = document.createElement("Row"); row.forEach((key, value) -> { if (value != null) { Element newKey = document.createElement((String) key); newKey.setTextContent(value.toString()); newNode.appendChild(newKey); } }); rootElement.appendChild(newNode); } document.appendChild(rootElement); /* * If you need return string view instead org.w3c.dom.Document */ StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); DOMSource domSource = new DOMSource(document); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); transformer.transform(domSource, result); // return document // return writer.toString() } } }