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

时间:2009-10-08 11:25:49

标签: java xml

我正在搜索轻量级API(首选单个类)来转换

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

到xml,反之亦然,将XML转换回Map。

示例:

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

我不想使用JAXBJSON conversion API。它不需要处理嵌套的地图或属性或其他任何东西,只是这个简单的情况。 有什么建议吗?


修改:我创建了一份工作副本&amp;粘贴样品。感谢fvuMichal Bernhard

Download latest XStream framework,“仅核心”就足够了。

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-x.y.z.jar即可。

13 个答案:

答案 0 :(得分:61)

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

    }

}

答案 1 :(得分:43)

此处XStream的转换器包括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<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;
}

答案 2 :(得分:8)

XStream怎么样?对于包括你在内的许多用例而言,不是1级而是2个罐子,使用起来非常简单但非常强大。

答案 3 :(得分:8)

一种选择是自己动手。这样做很简单:

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的格式不太了解,可以查看Properties

答案 4 :(得分:6)

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

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

}

但是我将maps值的序列化更改为委托给MarshallingContext。这应该改进解决方案以适用于复合地图值和嵌套地图。

答案 5 :(得分:3)

我编写了一段代码,将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"
        }
     }
 }

我没有逆过程,但写起来一定不是很难。

答案 6 :(得分:2)

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

你的数据很可能属于属性,但很难找到使用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实例设置,解析和存储:

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

答案 7 :(得分:2)

我在google上发现了这个,但我不想使用XStream,因为它会导致我的环境中出现很多开销。我只需要解析一个文件,因为我没有找到任何我喜欢的东西,所以我创建了自己的简单解决方案来解析你描述的格式的文件。所以这是我的解决方案:

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断言测试:

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

答案 8 :(得分:1)

我尝试了不同类型的地图,Conversion Box也有效。我已经使用了你的地图并在下面粘贴了一些内部地图。希望它对你有所帮助....

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

答案 9 :(得分:1)

Underscore-java库可以将Map转换为xml。我是该项目的维护者。 Live example

代码示例:

import com.github.underscore.lodash.U;
import java.util.*;

public class Main {

  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(U.toXml(map));

    //  <?xml version="1.0" encoding="UTF-8"?>
    //  <root>
    //    <name>chris</name>
    //    <island>faranga</island>
    //  </root>

    // and back:
    map = (Map<String, Object>) U.fromXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>"
        + "    <name>chris</name>"
        + "    <island>faranga</island>"
        + "  </root>");

    System.out.println(map);
    // {name=chris, island=faranga}
  }
}

答案 10 :(得分:1)

如果您只需要将简单地图转换为xml,而没有嵌套属性,那么轻量级解决方案将只是一个私有方法,如下所示:

private String convertMapToXML(Map<String, String> map) {
    StringBuilder xmlBuilder = new StringBuilder();
    xmlBuilder.append("<xml>");
    for (Map.Entry<String, String> entry : map.entrySet()) {
        if (entry.getValue() != null) {
            String xmlElement = entry.getKey();
            xmlBuilder.append("<");
            xmlBuilder.append(xmlElement);
            xmlBuilder.append(">");
            xmlBuilder.append(entry.getValue());
            xmlBuilder.append("<");
            xmlBuilder.append("/");
            xmlBuilder.append(xmlElement);                
            xmlBuilder.append(">");
        }             
    }

    xmlBuilder.append("</xml>");
    return xmlBuilder.toString();
}

答案 11 :(得分:0)

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

转换器支持嵌套映射:

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

答案 12 :(得分:0)

就我而言,我在Camel ctx中将DBresponse转换为XML。 JDBC执行程序返回具有LinkedCaseInsensitiveMap(单行)的ArrayList(行)。 任务-基于DBResponce创建XML对象。

UPDATE table