Jackson:XML到Map的列表反序列化

jfewjypa  于 2022-11-08  发布在  Java
关注(0)|答案(4)|浏览(265)

是否有方法使用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);

并获得了以下Map。列表中的第一项丢失。

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

iezvtpos1#

这是一个已知的jackson-dataformat-xml错误,在issue 205下归档。简单地说,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;

        }

    }

}
6jjcrrmo

6jjcrrmo2#

如果你必须使用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 -〉JSON对象(使用org.json)-〉字符串-〉JSON节点(使用readTree)
当然,toJSONObject处理重复没有任何问题,我建议如果可以的话尽量避免使用Jackson和readTree()

vhmi4jdf

vhmi4jdf3#

通过扩展UntypedObjectDeserializer创建了一个自定义反序列化程序来执行此工作。

bsxbgnwa

bsxbgnwa4#

如果您没有绑定到Jackson,underscore-java可以很好地完成此操作:

import com.github.underscore.U;
import java.util.Map;

public class MyClass {
    public static void main(String args[]) {
      Map map = U.fromXmlMap(
          "<order>\r\n" +
            "<number>12345678</number>\r\n" +
            "<amount>100.10</amount>\r\n" +
            "<items>\r\n" +
                "<item>\r\n" +
                    "<itemId>123</itemId>\r\n" +
                    "<amount>100.0</amount>\r\n" +
                    "<itemName>Item Name1</itemName>\r\n" +
                "</item>\r\n" +
                "<item>\r\n" +
                    "<itemId>234</itemId>\r\n" +
                    "<amount>200.00</amount>\r\n" +
                    "<itemName>Item Name1</itemName>\r\n" +
                "</item>\r\n" +
            "</items>\r\n" +
        "</order>"
      );
      System.out.println(map);
      // {order={number=12345678, amount=100.10, items={item=[{itemId=123, amount=100.0, itemName=Item Name1}, {itemId=234, amount=200.00, itemName=Item Name1}]}}, #omit-xml-declaration=yes}
    }
}

相关问题