3

我正在尝试使用 XStream 和 Jettison 序列化和反序列化番石榴的多图。这里有一个简单的测试来说明:

    final XStream xstream = new XStream(new JettisonMappedXmlDriver());
    final Multimap<TestEnum, String> test = HashMultimap.create();
    test.put(TestEnum.E1, "test");
    final String json = xstream.toXML(test);
    final Multimap<TestEnum, String> result = (Multimap<TestEnum, String>)xstream.fromXML(json);

它给出了以下错误:

com.thoughtworks.xstream.converters.ConversionException: Could not call com.google.common.collect.HashMultimap.readObject() : com.test.Test$TestEnum cannot be cast to java.lang.Integer
---- Debugging information ----
message             : Could not call com.google.common.collect.HashMultimap.readObject()
cause-exception     : java.lang.ClassCastException
cause-message       : com.test.Test$TestEnum cannot be cast to java.lang.Integer
class               : com.google.common.collect.HashMultimap
required-type       : com.google.common.collect.HashMultimap
converter-type      : com.thoughtworks.xstream.converters.reflection.SerializableConverter
path                : /com.google.common.collect.HashMultimap/com.google.common.collect.HashMultimap
line number         : -1
version             : 1.4.7
-------------------------------

请注意,当与枚举键一起使用时,此错误尤其集中在 Multimap 上。如果我使用 Map 而不是 multimap,则没有错误。如果我使用 String 而不是 Enum 作为键,则没有错误。此外,如果我序列化为 XML 而不是 JSON(即,在构造函数中没有“JettisonMappedXmlDriver”),它可以完美地工作。

有没有好的解决方案?我目前正在使用一种解决方法,用集合地图替换我的多图,但我更愿意找到一种方法来保留多图。

4

1 回答 1

0

使用 XStream 序列化 Multimap 的解决方案是使用 Multimap 转换器并注册到 XStream,如下所示:

public class MultimapConverter extends AbstractCollectionConverter {

    public MultimapConverter(Mapper mapper) {
        super(mapper);
    }

    @Override
    public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
        return Multimap.class.isAssignableFrom(clazz);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void marshal(
            Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
        Multimap map = (Multimap) value;
        for (Object key : map.keySet()) {
            for (Object v : map.get(key)) {
                if (v != null) {
                    writer.startNode("entry");
                    writer.addAttribute("key", key.toString());
                    writeCompleteItem(v, context, writer);
                    writer.endNode();
                }
            }
        }
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        ArrayListMultimap<Object, Object> map = ArrayListMultimap.create();
        while (reader.hasMoreChildren()) {
            reader.moveDown();
            String key = reader.getAttribute("key");
            Object value = null;
            while (reader.hasMoreChildren()) {
                reader.moveDown();
                value = readBareItem(reader, context, map);
                reader.moveUp();
            }
            reader.moveUp();

            if (value != null) {
                map.put(key, value);
            }
        }
        return map;
    }

}

然后将其注册到您拥有的 XStream 序列化程序中,例如:

XStream xstream = new XStream(new JettisonMappedXmlDriver());
xstream.registerConverter(new MultimapConverter(xstream.getMapper()));
xstream.allowTypeHierarchy(Multimap.class);
xstream.addDefaultImplementation(ArrayListMultimap.class, Multimap.class);

至于反序列化上述转换器的错误按预期工作:

@Test
public void testSerializeWithEnum() {
   XStream xstream = new XStream(new JettisonMappedXmlDriver());
   xstream.registerConverter(new MultimapConverter(xstream.getMapper()));
   xstream.allowTypeHierarchy(Multimap.class);
   xstream.addDefaultImplementation(ArrayListMultimap.class, Multimap.class);
   Multimap<TestEnum, String> test = HashMultimap.create();
   test.put(TestEnum.E1, "test");
   String json = xstream.toXML(test);
   final Multimap<TestEnum, String> result = (Multimap<TestEnum, String>)xstream.fromXML(json);
}

public enum TestEnum {
    E1
}

如果您在此处放置断点并调试result变量,您会看到 JSON 已使用枚举值反序列化。

于 2021-03-31T04:50:19.523 回答