【问题标题】:Convert XML to Map of nested maps将 XML 转换为嵌套地图的地图
【发布时间】:2014-04-09 04:26:39
【问题描述】:

我有一个带有嵌套元素和重复标签的 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>

现在我想要一个 API,它将这个 XML 转换为 Java 中的地图:

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

我知道我们可以使用 XStream API 来实现这一点。在这里,我只是想知道使用 XStream 是否有任何缺点,以及是否存在任何其他更好的 Java API 来实现这一点。有什么建议吗?

注意:这应该以通用方式完成,即 Java API 应该适用于任何 XML,而不仅仅是上述 XML。

【问题讨论】:

  • 嘿,如果 API 对您有用,请使用它,而且仅供参考,这最终可能主要是基于意见的。

标签: java xml spring jaxb xstream


【解决方案1】:

我认为 Melih 可能指的是这个解决方案。下面是一些使用 json.org 库的 Kotlin 代码:

将implementation "org.json:json:20180130" 添加到build.gradle。

import org.json.JSONML

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

【讨论】:

    【解决方案2】:

    虽然已经很晚了。但是我已经为 XStream API 编写了一个自定义的 MapEntryConverter,它可以处理任何复杂的 XML 数据,无论它有多深。即使它会支持重复标签名称(将存储为 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

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

    请注意,如果 Map 中有任何子项,则 Map 中的每个值都存储为一个列表。在我看来,解组部分非常整洁。我会在空闲时间完善和简化编组部分。

    输入数据应在 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>
    

    将我的输出与您的预期输出进行比较时,会有一些变化。重复的键值不会合并为一个数组,而是单独的列表。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我正在寻找一个 java API 来代替 SAX 和 DOM 解析器。
      • 没有标准的 API 可以做到这一点。执行此操作的任何代码都会在后台调用解析器,可能是 SAX 解析器。并且使用 SAX 来驱动树生成器——这本质上就是你所要求的——非常简单;如果您遇到问题,请查看 SAX-to-DOM 适配器(作为示例程序广泛使用)并根据您的需要对其进行修改。
      【解决方案4】:

      我曾经使用https://github.com/douglascrockford/JSON-java这个项目将xml转换为json。 它速度快,而且能胜任。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-11-12
        • 1970-01-01
        • 2019-12-23
        • 1970-01-01
        • 2016-11-14
        • 2015-10-16
        • 1970-01-01
        相关资源
        最近更新 更多