tags:

views:

1590

answers:

4

I'm searching a lightweight API (preferable single class) to convert a

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

to xml and, vice versa, convert the XML back to a Map.

example:

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

MagicAPI.toXML(map,"root");

result:

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

and back:

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

I dont want to use JAXB or JSON conversion API. It doesnt have to take care of nested maps or attributes or anything else, just that simple case. Any suggestions?


Edit: I created a working copy & paste sample. Thanks to fvu and Michal Bernhard.

Download latest XStream framework, 'core only' is enough.

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

No converters or anything else is required. Just the xstream-x.y.z.jar is enough.

A: 

You could build something yourself quite easily, here's a first start. Note that some things are missing; you should escape characters like < and > in the root, keys and values and take proper care of character encodings; you'll have to add that yourself.

public class MagicAPI {
    public static String toXML(Map<String, String> map, String root) {
        StringBuilder sb = new StringBuilder("<");
        sb.append(root);
        sb.append(">");

        for (Map.Entry<String, String> e : map.entrySet()) {
            sb.append("<");
            sb.append(e.getKey());
            sb.append(">");

            sb.append(e.getValue());

            sb.append("</");
            sb.append(e.getKey());
            sb.append(">");
        }

        sb.append("</");
        sb.append(root);
        sb.append(">");

        return sb.toString();
    }
}
Jesper
how about de-serialization?
Chris
You could do that with a simple SAX parser.
Jesper
I'd also recommend rolling your own. But I'd build a Document and splat that out. No need to worry about escaping values and the like.
mlk
I'd strongly recommend not attempting to create XML with with ad hoc string manipulation.
Tom Hawtin - tackline
At least use StAX instead of rolling your own: http://java.sun.com/javase/6/docs/api/javax/xml/stream/package-summary.html
McDowell
+5  A: 

How about XStream? Not 1 class but 2 jars for many use cases including yours, very simple to use yet quite powerful.

fvu
+2  A: 

One option would be to roll your own. It would be fairly simple to do:

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

and the load is an equally simply getChildNodes and a loop. Sure it has a bit of boiler plate that the XML Gods demand but it is at most 1 hours work.

Or you could look at Properties if you are not too fused about the format of the XML.

mlk
+3  A: 

XStream!

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.alias("root", Map.class);
        magicApi.registerConverter(new MapEntryConverter());

        String xml = magicApi.toXML(map);
        System.out.println("Result of tweaked XStream toXml()");
        System.out.println(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());
                writer.setValue(entry.getValue().toString());
                writer.endNode();
            }
        }

        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
            // dunno, read manual and do it yourself ;)
        }

    }

}
Michal Bernhard
well, it works without a converter too, but u are right, thats it
Chris