Pregunta

Here's a simplified class:

class Foo implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer id;
    private Set<Foo> children;

    public Foo( Integer id ) {
        if( id == null ) {
            throw new IllegalArgumentException( );
        }
        this.id = id;
        this.children = new HashSet<Foo>( 16 );
    }
    @Override public int hashCode( ) {
        return id.hashCode( );
    }

    ...
}

As you can see, it contains a set of itself, and uses its id property to generate a hash. But I have an issue when the object has a self-referential loop:

When the object is de-serialised, processing follows the children to the deepest object first, then builds backwards. That's usually fine, but if an object contains one of the higher objects in its children set it breaks: When it tries to add this object to its HashSet, it calls hashCode, but id hasn't been loaded for that object yet, so it crashes with a NullPointerException.

(It took me quite a while to track that down!)

So my question is: can I control the order of serialisation? I need to have id serialised (and de-serialised) before children.

¿Fue útil?

Solución 2

I followed jdev's suggestion and (after some research) implemented my own serialization using the Externalizable interface, like this:

class Foo implements Externalizable {
    /**
     * DO NOT USE THIS CONSTRUCTOR! This only exists for Externalizable
     */
    public Foo( ) {
        id = null;
        children = null;
    }

    @Override public void writeExternal( final ObjectOutput o ) throws IOException {
        o.writeInt( id.intValue( ) );
        o.writeObject( children );
    }

    @SuppressWarnings( "unchecked" )
    @Override public void readExternal( final ObjectInput o ) throws IOException, ClassNotFoundException {
        id = Integer.valueOf( o.readInt( ) );
        children = (Set<Foo>) o.readObject( );
    }

    // rest of code as before
}

My only gripe is that it needs a public no-param constructor now, but I can live with that (this is an internal-only API at least).

Now that everything's done in the correct order, I have no issues de-serialising.

Otros consejos

Your analysis seems to be correct. You should probably implement a custom serialization logic. Assuming the ids are unique, consider the following:

  • When serializing, for each node store its id plus a list of children ids (you may want to add the other fields you omitted here)
  • When de-serializing, maintain an auxiliar mapping of visited ids to their records. Scan the serialized list sequentially, and for each new id you encounter (either parent or child), create a new record (with id field initialized) and store it in the map. Children can now be added safely to the parent's hashset, since they have their id field initialized already.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top