Inner classes that subclass LinkedList are not unmarshellable

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Inner classes that subclass LinkedList are not unmarshellable

Joseph Campolongo
I have an inner class that subclasses LinkedList:

import java.util.LinkedList;
import com.thoughtworks.xstream.XStream;

public class ListTest
{
  public void printSomething() {
    System.out.println("Something");
  }
 
  protected class Foo extends LinkedList {
    public boolean add(Object o)
    {
      printSomething();
      return super.add(o);
    }
   
  }
}

If I try to serialize and deserialize this class, after adding an item to
Foo, using XStream (e.g.

  public static void main(String[] args)
  {
    ListTest lt = new ListTest();
    Foo foo = lt.new Foo();
   
    foo.add("One");
    foo.add("Two");

    XStream xs = new XStream();
    String s = xs.toXML(foo);
   
    Foo fooCopy = (Foo)xs.fromXML(s);
    System.out.println("COMPLETED!");
  }

)

I get a NullPointerException.  The problem is that, in Foo.add(), I make a
call to 'printSomething()', which is really a call to
ListTest.this.printSomething(), when LinkTest.this just happens to be null.
This occurs because Foo's ListTest.this isn't initialized until after the
constructor for Foo completes.

Is there a way to work around this?

Here is the output from the above:

Something
Something
com.thoughtworks.xstream.converters.ConversionException: Could not call
ListTest$Foo.readObject()
---- Debugging information ----
required-type       : ListTest$Foo
cause-message       : null
class               : ListTest$Foo
message             : Could not call ListTest$Foo.readObject()
line number         : 5
path                : /ListTest-Foo/linked-list
cause-exception     : java.lang.reflect.InvocationTargetException
-------------------------------
        at
com.thoughtworks.xstream.converters.reflection.SerializationMethodInvoker.ca
llReadObject(SerializationMethodInvoker.java:75)
        at
com.thoughtworks.xstream.converters.reflection.SerializableConverter.unmarsh
al(SerializableConverter.java:360)
        at
com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshall
er.java:38)
        at
com.thoughtworks.xstream.core.ReferenceByXPathUnmarshaller.convertAnother(Re
ferenceByXPathUnmarshaller.java:39)
        at
com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:9
9)
        at
com.thoughtworks.xstream.core.ReferenceByXPathMarshallingStrategy.unmarshal(
ReferenceByXPathMarshallingStrategy.java:12)
        at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:521)
        at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:509)
        at com.thoughtworks.xstream.XStream.fromXML(XStream.java:475)
        at com.thoughtworks.xstream.XStream.fromXML(XStream.java:468)
        at ListTest.main(ListTest.java:22)
Caused by: java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at
com.thoughtworks.xstream.converters.reflection.SerializationMethodInvoker.ca
llReadObject(SerializationMethodInvoker.java:71)
        ... 10 more
Caused by: java.lang.NullPointerException
        at ListTest$Foo.add(ListTest.java:33)
        at java.util.LinkedList.readObject(Unknown Source)
        ... 15 more
Exception in thread "main"

Reply | Threaded
Open this post in threaded view
|

Re: Inner classes that subclass LinkedList are not unmarshellable

Jörg Schaible-2
Hi Joseph,

Joseph Campolongo wrote:

> I have an inner class that subclasses LinkedList:
>
> import java.util.LinkedList;
> import com.thoughtworks.xstream.XStream;
>
> public class ListTest
> {
>   public void printSomething() {
>     System.out.println("Something");
>   }
>  
>   protected class Foo extends LinkedList {
>     public boolean add(Object o)
>     {
>       printSomething();
>       return super.add(o);
>     }
>    
>   }
> }
>
> If I try to serialize and deserialize this class, after adding an item to
> Foo, using XStream (e.g.
>
>   public static void main(String[] args)
>   {
>     ListTest lt = new ListTest();
>     Foo foo = lt.new Foo();
>    
>     foo.add("One");
>     foo.add("Two");
>
>     XStream xs = new XStream();
>     String s = xs.toXML(foo);
>    
>     Foo fooCopy = (Foo)xs.fromXML(s);
>     System.out.println("COMPLETED!");
>   }
>
> )
>
> I get a NullPointerException.  The problem is that, in Foo.add(), I make a
> call to 'printSomething()', which is really a call to
> ListTest.this.printSomething(), when LinkTest.this just happens to be
> null. This occurs because Foo's ListTest.this isn't initialized until
> after the constructor for Foo completes.
>
> Is there a way to work around this?
>
> Here is the output from the above:
>
> Something
> Something
> com.thoughtworks.xstream.converters.ConversionException: Could not call
> ListTest$Foo.readObject()
> ---- Debugging information ----
> required-type       : ListTest$Foo
> cause-message       : null
> class               : ListTest$Foo
> message             : Could not call ListTest$Foo.readObject()
> line number         : 5
> path                : /ListTest-Foo/linked-list
> cause-exception     : java.lang.reflect.InvocationTargetException
> -------------------------------

Well, you don't get a NulklPointerException, but a ConversionException.
XStream just don't know, how to marshal your derived List. Add a Converter
for the type ListTest.Foo and your example should work.

- Jörg

Reply | Threaded
Open this post in threaded view
|

Re: Inner classes that subclass LinkedList are not unmarshellable

Joseph Campolongo
> Joseph Campolongo wrote:
>
> > I have an inner class that subclasses LinkedList:
> >
> > import java.util.LinkedList;
> > import com.thoughtworks.xstream.XStream;
> >
> > public class ListTest
> > {
> >   public void printSomething() {
> >     System.out.println("Something");
> >   }
> >  
> >   protected class Foo extends LinkedList {
> >     public boolean add(Object o)
> >     {
> >       printSomething();
> >       return super.add(o);
> >     }
> >    
> >   }
> > }
> >

[SNIP]

> > com.thoughtworks.xstream.converters.ConversionException: Could not call
> > ListTest$Foo.readObject()
> > ---- Debugging information ----
> > required-type       : ListTest$Foo
> > cause-message       : null
> > class               : ListTest$Foo
> > message             : Could not call ListTest$Foo.readObject()
> > line number         : 5
> > path                : /ListTest-Foo/linked-list
> > cause-exception     : java.lang.reflect.InvocationTargetException
> > -------------------------------
>


> Well, you don't get a NulklPointerException, but a ConversionException.
> XStream just don't know, how to marshal your derived List. Add a Converter
> for the type ListTest.Foo and your example should work.


The underlying exception is a NullPointerException (see the cause-exception)
because the containing outer class isn't set before the 'add()' method is called.

In other words, 'readObject()' couldn't be called because of a
NullPointerException in readObject(), caused because readObject() calls 'add()',
which, in the above case, calls out to its containing class, which happens to be
null.

In order to work around this problem, I created a converter that extended
CollectionConverter and overrode the 'populateCollection' method.  Instead of
calling 'add()' to add the items to the collection, I stored both the collection
& a list of the items in hashmaps keyed by the collections system hashcode.

Then, after XStream finished, I looped through these hashmaps and added the
items to the collections.

Not the simplest or cleanest solution, but I wasn't sure how else to approach it.

Joseph Campolongo



Reply | Threaded
Open this post in threaded view
|

Re: Inner classes that subclass LinkedList are not unmarshellable

Jörg Schaible-2
Hi Joseph,

Joseph Campolongo wrote:

> The underlying exception is a NullPointerException (see the
> cause-exception) because the containing outer class isn't set before the
> 'add()' method is called.
>
> In other words, 'readObject()' couldn't be called because of a
> NullPointerException in readObject(), caused because readObject() calls
> 'add()', which, in the above case, calls out to its containing class,
> which happens to be null.

You're right, but this is not a problem of XStream per se, but of the
implementation of LinkedList and your overloaded add method:

        ListTest lt = new ListTest();
        ListTest.Foo foo = lt.new Foo();

        foo.add("One");
        foo.add("Two");

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(os);
        out.writeObject(foo);
        out.close();
        ByteArrayInputStream is = new
ByteArrayInputStream(os.toByteArray());
        ObjectInputStream in = new ObjectInputStream(is);
        ListTest.Foo fooCopy = (ListTest.Foo)in.readObject();
        in.close();
       
        System.out.println("COMPLETED!");

This fails exactly with the same NullPointerException.

> In order to work around this problem, I created a converter that extended
> CollectionConverter and overrode the 'populateCollection' method.  Instead
> of calling 'add()' to add the items to the collection, I stored both the
> collection & a list of the items in hashmaps keyed by the collections
> system hashcode.
>
> Then, after XStream finished, I looped through these hashmaps and added
> the items to the collections.
>
> Not the simplest or cleanest solution, but I wasn't sure how else to
> approach it.

You might check "ListTest.this == null" in your add implementation before
accessing methods/fields on the outer instance, assuming that the
unmarshalling will also restore any settings of the outer instance later on
to keep it consistent.

- Jörg