Come convertire XML in java.util.Map e viceversa

Sto cercando un’API leggera (preferibile una singola class) per convertire a

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

a xml e, viceversa, riconvertire l’XML in una mappa.

esempio:

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

risultato:

  chris faranga  

e ritorno:

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

Non voglio usare l’ API di conversione JAXB o JSON . Non deve prendersi cura di mappe nidificate o attributi o qualsiasi altra cosa, solo questo caso semplice. Eventuali suggerimenti?


Modifica : ho creato un campione di copia e incolla funzionante. Grazie a Fvu e Michal Bernhard .

Scarica l’ultimo framework XStream , ‘core only’ è sufficiente.

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

Nessun convertitore o altro è richiesto. Solo xstream-xyzjar è sufficiente.

XStream!

Aggiornato : ho aggiunto la parte unmarshal come richiesto nei commenti ..

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

Qui il convertitore per XStream includendo unmarshall

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

Che ne dici di XStream ? Non 1 class ma 2 vasi per molti casi d’uso incluso il tuo, molto semplice da usare ma abbastanza potente.

Un’opzione sarebbe quella di tirare il tuo. Sarebbe abbastanza semplice da fare:

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

e il carico è ugualmente semplicemente getChildNodes e un ciclo. Certo, ha un po ‘di piastra di riscaldamento che gli Dei XML richiedono ma è al massimo 1 ora di lavoro.

Oppure puoi guardare le Proprietà se non sei troppo fuso sul formato dell’XML.

Ho usato l’approccio con il convertitore personalizzato:

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

Ma ho cambiato la serializzazione del valore delle mappe per debind a MarshallingContext. Ciò dovrebbe migliorare la soluzione di lavoro anche per i valori di mappa composita e le mappe nidificate.

Ho scritto un pezzo di codice che trasforma un contenuto XML in una struttura multistrato di mappe:

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

Questo codice trasforma questo:

  blue  wood
wood

in

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

Non ho il processo inverso, ma non deve essere molto difficile da scrivere.

Sto postando questa risposta come risposta non perché è la risposta corretta alla tua domanda, ma perché è una soluzione allo stesso problema, ma utilizza invece gli attributi. Altrimenti la risposta di Vikas Gujjar è corretta.

Piuttosto, i tuoi dati potrebbero essere in attributi, ma è abbastanza difficile trovare esempi di lavoro usando XStream per fare questo, quindi eccone uno:

Dati di esempio:

       

Implementazione di MapEntryConverter (leggermente rielaborata l’implementazione di @Vikas Gujjar per utilizzare gli attributi):

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

Installazione dell’istanza XStream, analisi e memorizzazione:

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

L’ho trovato su google, ma non voglio usare XStream perché causa un sovraccarico nel mio ambiente. Ho solo dovuto analizzare un file e dato che non ho trovato nulla che mi piacesse ho creato la mia semplice soluzione per analizzare un file del formato che descrivi. Quindi ecco la mia soluzione:

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

Qui ci sono un paio di Test Test GG + FEST:

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

Ho provato diversi tipi di mappe e la Conversion Box ha funzionato. Ho usato la tua mappa e ho incollato un esempio qui sotto con alcune mappe interne. Spero ti sia d’aiuto ….

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

Produzione:

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

La libreria Underscore-java può convertire Map in xml. Esempio dal vivo

Esempio di codice:

 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 //  } } 

Ora è il 2017, l’ultima versione di XStream richiede un convertitore per farlo funzionare come previsto.

Un convertitore supporta la mappa nidificata:

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

Nel mio caso, converto DBresponse in XML in Camel ctx. L’esecutore JDBC restituisce ArrayList (righe) con LinkedCaseInsensitiveMap (riga singola). Attività: crea un object XML basato su 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.Transformsr; import javax.xml.transform.TransformsrFactory; 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); TransformsrFactory tf = TransformsrFactory.newInstance(); Transformsr transformsr = tf.newTransformsr(); transformsr.setOutputProperty(OutputKeys.INDENT, "yes"); transformsr.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); transformsr.transform(domSource, result); // return document // return writer.toString() } } }