Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle cyclic references in XStream?

The XStream serialization library claims to be robust in serializing complex Java objects out of the box (e.g. no modifications to objects or mapping needed). In particular, according to XStream docs, XStream can handle circular object references.

So I wrote a test to check this out - I tried to serialize a somewhat complex data structure (LinkedHashMultimap) that included a reference to iteself:

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:

<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 to xstream.fromXML()) fails with a NullPointerException exception:

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 resolve this?

like image 498
mchen Avatar asked Jan 05 '15 10:01

mchen


1 Answers

Use:

XStream xstream = new XStream();
xstream.setMode(XStream.XPATH_RELATIVE_REFERENCES);
like image 70
Sachin Verma Avatar answered Nov 14 '22 19:11

Sachin Verma