How do I handle circular references in XStream?
the XStream serialization library claims to be reliable for serializing complex Java objects out of the box (no object changes or need for display, for example). In particular, according to the XStream docs , XStream can handle circular object references.
So, I wrote a test to check this - I tried to serialize a somewhat complex data structure ( LinkedHashMultimap
) that included a reference to itself:
import com.google.common.collect.LinkedHashMultimap;
import com.thoughtworks.xstream.XStream;
public class SerializationTest {
public static void main(String[] args0) {
// Create LinkedHashMultimap to serialize
LinkedHashMultimap<String, Object> outObj = LinkedHashMultimap.create();
outObj.put("x", 1);
outObj.put("x", "abc");
outObj.put("y", outObj); // Add a self-reference
// Try to serialize
XStream xstream = new XStream();
String xml = xstream.toXML(outObj);
System.out.println(xml); // Print XML to console for a quick peek
// Try to deserialize - ERROR HERE!!!
LinkedHashMultimap<String, Object> inObj = (LinkedHashMultimap<String, Object>)xstream.fromXML(xml);
System.out.println(inObj);
}
}
The serialized XML object looks like this:
<com.google.common.collect.LinkedHashMultimap serialization="custom">
<unserializable-parents/>
<com.google.common.collect.LinkedHashMultimap>
<default/>
<int>2</int>
<int>2</int>
<string>x</string>
<string>y</string>
<int>3</int>
<string>x</string>
<int>1</int>
<string>x</string>
<string>abc</string>
<string>y</string>
<com.google.common.collect.LinkedHashMultimap reference="../.."/>
</com.google.common.collect.LinkedHashMultimap>
</com.google.common.collect.LinkedHashMultimap>
However, deserialization (i.e. the call xstream.fromXML()
) fails with an exception NullPointerException
:
Exception in thread "main" com.thoughtworks.xstream.converters.ConversionException: Could not call com.google.common.collect.LinkedHashMultimap.readObject() : null
---- Debugging information ----
message : Could not call com.google.common.collect.LinkedHashMultimap.readObject()
cause-exception : java.lang.NullPointerException
cause-message : null
class : com.google.common.collect.LinkedHashMultimap
required-type : com.google.common.collect.LinkedHashMultimap
converter-type : com.thoughtworks.xstream.converters.reflection.SerializableConverter
path : /com.google.common.collect.LinkedHashMultimap/com.google.common.collect.LinkedHashMultimap
line number : 15
version : 1.4.7
-------------------------------
at com.thoughtworks.xstream.converters.reflection.SerializationMethodInvoker.callReadObject(SerializationMethodInvoker.java:119)
at com.thoughtworks.xstream.converters.reflection.SerializableConverter.doUnmarshal(SerializableConverter.java:454)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:257)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:65)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:66)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:50)
Disconnected from the target VM, address: '127.0.0.1:50107', transport: 'socket'
at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:134)
at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1185)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1169)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1040)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1031)
at SerializationTest3.main(SerializationTest3.java:18)
Caused by: java.lang.NullPointerException
at com.google.common.collect.AbstractMapBasedMultimap$AsMap.hashCode(AbstractMapBasedMultimap.java:1289)
at com.google.common.collect.AbstractMultimap.hashCode(AbstractMultimap.java:228)
at com.google.common.collect.LinkedHashMultimap.hashCode(LinkedHashMultimap.java:81)
at com.google.common.collect.Hashing.smearedHash(Hashing.java:51)
at com.google.common.collect.LinkedHashMultimap$ValueSet.add(LinkedHashMultimap.java:416)
at com.google.common.collect.LinkedHashMultimap.readObject(LinkedHashMultimap.java:574)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.thoughtworks.xstream.converters.reflection.SerializationMethodInvoker.callReadObject(SerializationMethodInvoker.java:113)
... 13 more
So does anyone know why this is happening? And how to solve this?
source to share