简体   繁体   English

将XML转换为嵌套地图的Map

[英]Convert XML to Map of nested maps

I have an XML with nested elements and repeating tags. 我有一个带有嵌套元素和重复标记的XML。 For example: 例如:

<person>
    <name>Rama</name>
    <age>27</age>
    <gender>male</gender>
    <address>
        <doornumber>234</doornumber>
        <street>Kanon</street>
        <city>Hyderabad</city>
    </address>
    <qualification>
        <degree>M.Sc</degree>
        <specialisation>Maths</specialisation>
    </qualification>
    <qualification>
        <degree>B.E.</degree>
        <specialisation>Electrical</specialisation>
    </qualification>
</person>

Now I want an API which will convert this XML into a Map of Maps in Java: 现在我想要一个API,它将这个XML转换成Java中的Map地图:

{name="Rama",age="27",gender="male",address={doornumber=234,street="Kanon",city="Hyderabad"},qualification=[{degree="M.Sc",specialisation="Maths"},{degree="B.E.",specialisation="Electrical"}]}

I know that we can use XStream API to achieve this. 我知道我们可以使用XStream API来实现这一目标。 Here I just wanted to know if using XStream has any downsides and whether there exists any other better Java API for achieving this. 在这里,我只是想知道使用XStream是否有任何缺点以及是否存在任何其他更好的Java API来实现这一点。 Any suggestions? 有什么建议?

Note: This should be done in a generic way ie the Java API should be applicable to any XML, not just to the above XML. 注意:这应该以通用的方式完成,即Java API应该适用于任何XML,而不仅仅适用于上述XML。

It's very late though. 但现在已经很晚了。 But I've written a custom MapEntryConverter for XStream API which can handly any complexity of XML data, no matter how deep it is. 但是我已经为XStream API编写了一个自定义的MapEntryConverter,它可以处理任何复杂的XML数据,无论它有多深。 Even it'll support Duplicate tag names (which will be stored as ArrayList). 即使它支持重复标记名称(将存储为ArrayList)。

XStream xStream = new XStream(new DomDriver());
xStream.registerConverter(new MapEntryConverter());
xStream.alias("xml", java.util.Map.class);

// from XML, convert back to map
Map<String, List<Object>> map = (Map<String, List<Object>>) xStream.fromXML(xmlData);
/*System.out.println("MAP: \n" + map.entrySet().toString());*/

String xml = xStream.toXML(map);
/*System.out.println("XML: \n"+xml);*/

MapEntryConverter.java MapEntryConverter.java

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

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;

public class MapEntryConverter implements Converter{
    @SuppressWarnings("rawtypes")
    public boolean canConvert(Class clazz) {
        return AbstractMap.class.isAssignableFrom(clazz);
    }

    @SuppressWarnings({ "unchecked" })
    public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
        AbstractMap<String, List<?>> map = (AbstractMap<String, List<?>>) value;
        List<Map<String, ?>> list = (List<Map<String, ?>>) map.get("xml");
        for( Map<String, ?> maps: list ) {
            for( Entry<String, ?> entry: maps.entrySet() ) {
                mapToXML(writer, entry);
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void mapToXML(HierarchicalStreamWriter writer, Entry<String, ?> entry) {
        writer.startNode(entry.getKey());
        if( entry.getValue() instanceof String ) {
            writer.setValue(entry.getValue().toString());
        }else if(  entry.getValue() instanceof ArrayList ) {
            List<?> list = (List<?>) entry.getValue();
            for( Object object: list ) {
                Map<String, ?> map = (Map<String, ?>) object;
                for( Entry<String, ?> entryS: map.entrySet() ) {
                    mapToXML(writer, entryS);
                }
            }
        }
        writer.endNode();
    }

    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        Map<String, List<Object>> map = new HashMap<String, List<Object>>();
        map = xmlToMap(reader, new HashMap<String, List<Object>>());
        return map;
    }

    private Map<String, List<Object>> xmlToMap(HierarchicalStreamReader reader, Map<String, List<Object>> map) {
        List<Object> list = new ArrayList<Object>();
        while(reader.hasMoreChildren()) {
            reader.moveDown();
            if( reader.hasMoreChildren() ) {
                list.add(xmlToMap(reader, new HashMap<String, List<Object>>()));
            }else {
                Map<String, Object> mapN = new HashMap<String, Object>();
                mapN.put(reader.getNodeName(), reader.getValue());
                list.add(mapN);
            }
            reader.moveUp();
        }
        map.put(reader.getNodeName(), list);
        return map;
    }

}

Do note that each value in Map is stored as a list if it has any children. 请注意,如果Map中的每个值都有子项,则会将其存储为列表。 The unmarshalling part is quite neat according to me. 根据我的说法,解组部分非常整洁。 I'll polish and simplify the marshalling part in free time. 我会在空闲时间打磨和简化编组部分。

Input data should be provided within xml tag like below. 输入数据应该在xml标签中提供,如下所示。

<xml>
    <person>
        <name>Rama</name>
        <age>27</age>
        <gender>male</gender>
        <address>
            <doornumber>234</doornumber>
            <street>Kanon</street>
            <city>Hyderabad</city>
        </address>
        <qualification>
            <degree>M.Sc</degree>
            <specialisation>Maths</specialisation>
        </qualification>
        <qualification>
            <degree>B.E.</degree>
            <specialisation>Electrical</specialisation>
        </qualification>
    </person>
</xml>

There is a little change when comparing my output with your expected output. 将输出与预期输出进行比较时会有一点变化。 Duplicate key values are not merged as an array, instead they're of seperate list. 重复的键值不会合并为数组,而是它们是单独的列表。

I used https://github.com/douglascrockford/JSON-java this project for once to convert xml to json. 我使用https://github.com/douglascrockford/JSON-java这个项目一次将xml转换为json。 it is fast and does its job. 它很快并且完成了它的工作。

就个人而言,我会使用标准的JAXP接口 - 如果你真的想要一张地图的地图,那就是SAX,如果你想保留顺序的DOM(一棵树而不是无序的嵌套地图),并且想要更好地映射到XML信息集的API。

I think Melih might have been referring to this solution. 我认为Melih可能指的是这个解决方案。 Here is some Kotlin code using the json.org library: 这是使用json.org库的一些Kotlin代码:

Add implementation "org.json:json:20180130" to build.gradle . implementation "org.json:json:20180130"build.gradle

import org.json.JSONML

fun xmlToMap(xml: String): MutableMap<String, Any>? {
    return JSONML.toJSONObject(xml).toMap()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM