【问题标题】:Jackson: XML to Map with List deserializationJackson: XML to Map with List 反序列化
【发布时间】:2012-12-07 08:43:27
【问题描述】:

有没有办法使用 Jackson 将以下 xml 反序列化为包含项目列表的 Map?

<order>
    <number>12345678</number>
    <amount>100.10</amount>
    <items>
        <item>
            <itemId>123</itemId>
            <amount>100.0</amount>
            <itemName>Item Name1</itemName>
        </item>
        <item>
            <itemId>234</itemId>
            <amount>200.00</amount>
            <itemName>Item Name1</itemName>
        </item>
    </items>
</order>

我试过了

XmlMapper mapper = new XmlMapper();
LinkedHashMap map = (LinkedHashMap)mapper.readValue(xml, Object.class);

并得到以下地图。列表中的第一项丢失。

{
    order={
        number=12345678,
        amount=100.1,
        items={
            item={
                amount=200.0,
                itemName=ItemName2,
                itemId=234
            }
        }
    }
}

【问题讨论】:

  • 我不想使用 POJO 来保存订单数据。这里的想法是使用 Map 和 List 作为通用数据结构。
  • 当人们使用mapper.readTree(xml); 时也会出现这个“问题”,人们可能希望它建立一个树状图

标签: xml serialization jackson


【解决方案1】:

如果您必须使用readTree()JsonNode,其他答案将不起作用。我知道这是一个丑陋的解决方案,但至少你不需要在你的项目中粘贴某人的要点。

将 org.json 添加到您的项目依赖项中。

然后执行以下操作:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONObject;
import org.json.XML;
...
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
...
    JSONObject soapDatainJsonObject = XML.toJSONObject(data);
    return OBJECT_MAPPER.readTree(soapDatainJsonObject.toString());

转换如下:

XML -> JSONObject(使用 org.json)-> 字符串 -> JsonNode(使用 readTree)

当然,toJSONObject 处理重复没有任何问题,我建议尽可能避免使用 Jackson 和 readTree()

【讨论】:

    【解决方案2】:

    这是在issue 205 下提交的已知jackson-dataformat-xml 错误。简而言之,XML 中的重复元素被当前的UntypedObjectDeserializer 实现所吞噬。幸运的是,报告作者(João Paulo Varandas)也提供了a temporary fix in the form a custom UntypedObjectDeserializer implementation。下面我分享我对修复的解释:

    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonToken;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    
    import javax.annotation.Nullable;
    import java.io.IOException;
    import java.util.*;
    
    public enum JacksonDataformatXmlIssue205Fix {;
    
        public static void main(String[] args) throws IOException {
            String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                    "<items>\n" +
                    "    <item><id>1</id></item>\n" +
                    "    <item><id>2</id></item>\n" +
                    "    <item><id>3</id></item>\n" +
                    "</items>";
            SimpleModule module = new SimpleModule().addDeserializer(Object.class, Issue205FixedUntypedObjectDeserializer.getInstance());
            XmlMapper xmlMapper = (XmlMapper) new XmlMapper().registerModule(module);
            Object object = xmlMapper.readValue(xml, Object.class);
            System.out.println(object);     // {item=[{id=1}, {id=2}, {id=3}]}
        }
    
        @SuppressWarnings({ "deprecation", "serial" })
        public static class Issue205FixedUntypedObjectDeserializer extends UntypedObjectDeserializer {
    
            private static final Issue205FixedUntypedObjectDeserializer INSTANCE = new Issue205FixedUntypedObjectDeserializer();
    
            private Issue205FixedUntypedObjectDeserializer() {}
    
            public static Issue205FixedUntypedObjectDeserializer getInstance() {
                return INSTANCE;
            }
    
            @Override
            @SuppressWarnings({ "unchecked", "rawtypes" })
            protected Object mapObject(JsonParser parser, DeserializationContext context) throws IOException {
    
                // Read the first key.
                @Nullable String firstKey;
                JsonToken token = parser.getCurrentToken();
                if (token == JsonToken.START_OBJECT) {
                    firstKey = parser.nextFieldName();
                } else if (token == JsonToken.FIELD_NAME) {
                    firstKey = parser.getCurrentName();
                } else {
                    if (token != JsonToken.END_OBJECT) {
                        throw context.mappingException(handledType(), parser.getCurrentToken());
                    }
                    return Collections.emptyMap();
                }
    
                // Populate entries.
                Map<String, Object> valueByKey = new LinkedHashMap<>();
                String nextKey = firstKey;
                do {
    
                    // Read the next value.
                    parser.nextToken();
                    Object nextValue = deserialize(parser, context);
    
                    // Key conflict? Combine existing and current entries into a list.
                    if (valueByKey.containsKey(nextKey)) {
                        Object existingValue = valueByKey.get(nextKey);
                        if (existingValue instanceof List) {
                            List<Object> values = (List<Object>) existingValue;
                            values.add(nextValue);
                        } else {
                            List<Object> values = new ArrayList<>();
                            values.add(existingValue);
                            values.add(nextValue);
                            valueByKey.put(nextKey, values);
                        }
                    }
    
                    // New key? Put into the map.
                    else {
                        valueByKey.put(nextKey, nextValue);
                    }
    
                } while ((nextKey = parser.nextFieldName()) != null);
    
                // Ship back the collected entries.
                return valueByKey;
    
            }
    
        }
    
    }
    

    【讨论】:

    • UntypedObjectDeserializer 的构造函数被弃用了怎么办?
    • 好方法!我使用类似的方法,但使用番石榴多图加上特殊配置的编写器。 stackoverflow.com/a/62468955/1485527
    【解决方案3】:

    通过扩展 UntypedObjectDeserializer 来创建自定义反序列化器来完成这项工作。

    【讨论】:

    • 你能扩展这个答案吗?将不胜感激
    猜你喜欢
    • 2014-05-19
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 2017-01-23
    • 2013-12-05
    • 2018-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多