views:

124

answers:

4

Consider the following situation:

There is a serialization file, created by the older version of the application. Unfortunately, the package has changed for the class, that has been serialized. And now I need to load the information from this file into the same class, but located in different package. This class has serialVersionUID defined and has not changed (i.e. is compatible).

Question: Is it possible to load the new class instances from this file using any tricks (except trivial copying the class into old package and then using the deserialization wrapper logic)? It is possible to use readResolve() to recover from moving/renaming the class? If not, please, explain why.

+4  A: 

Probably your best bet is to recreate the old class (name, package and serial ID), read in the serialized form, then copy the data to an instance of the new object and reserialize that.

If you have a lot of these serialized objects, perhaps you could write a small script to do this so the "schema change" gets done in one go.

Another option is to resurrect the old class and implement its readResolve method to return an instance of the new class (perhaps by declaring a copy constructor). Personally I think I'd go for the schema change script and then delete the old class for good.

oxbow_lakes
+1 for the hint for injecting `readResolve()` into "old" class. But I assume with my question, that recovering of a class in old package is already considered and I ask for alternatives.
dma_k
+2  A: 

Question: Is it possible to load the new class instances from this file using any tricks (except trivial copying the class into old package and then using the deserialization wrapper logic)?

I don't think there are any other "tricks" you could use that don't involve at least a partial reimplementation of the serialization protocol.

It is possible to use readResolve() to recover from moving/renaming the class? If not, please, explain why.

No, because the deserialization mechanism will fail much earlier, at the stage where it tries to locate the class that's being deserialized - it has no way of knowing that a class in a different package has a readResolve() method it's supposed to use.

Michael Borgwardt
I agree with this answer, Also I would like to add, that overriding `ObjectInputStream#resolveClass(ObjectStreamClass)` will not help, if returned class has different full name from one, that was originally requested (and that is the case).
dma_k
@dma_k: actually it looks to me like you could achieve what you want by overriding that method
Michael Borgwardt
A: 

I don't think it's possible to do what you want.

Format of serialization file keeps class names. In detail it has next structure:

AC ED

protocol version number

object data

object's class description

Class description has next format:

full class name

serial version unique ID (SHA1 from fields and methods signatures)

serialization options

field descriptors

When you try to deserialize object serialization mechanism compares class names first (and you don't pass this step), then it compares serialVersionUID's and only after passing these 2 steps deserializes object.

Roman
I suppose, you mean that `object data` does after `object's class description` in a stream. Thanks for the answer!
dma_k
No, as I read in Horstmann's "Core Java Volume I" object data goes first.
Roman
+1  A: 

It is possible:

class HackedObjectInputStream extends ObjectInputStream {

    public HackedObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

        if (resultClassDescriptor.getName().equals("oldpackage.Clazz"))
            resultClassDescriptor = ObjectStreamClass.lookup(newpackage.Clazz.class);

        return resultClassDescriptor;
    }
}

This also allows one to ignore serialVersionUIDs mismatch or even deserialize a class if its field structure was changed.

Igor
@Igor: Thank you for your comments, +1. Indeed, this can be a solution, but I have no influence on the module, that does deserialization. So I can only trick `.ser` files, or the serialized class.
dma_k