I want to write the code, that can deserialize the class even if the class was changed (but you have old class version).
The idea is simple.
- Read the
serialVersionUIDof the serialized class - Check if that
serialVersionUIDis equal to theserialVersionUIDof the current class version. - If not, create new
ClassLoaderand load the old class version into workspace.
I thought to use something like this:
FileInputStream file = new FileInputStream(filename);
ObjectInputStream o = new ObjectInputStream(file) {
protected Class<?> resolveClass(java.io.ObjectStreamClass desc) throws IOException, ClassNotFoundException {
//5639490496474438904L is the suid of the older version
if (desc.getSerialVersionUID() == 5639490496474438904L) {
Class c01 = null;
URL eineURL = new URL("file://localhost/U:/MyJavaProj/bin/test/oldversion/");
URL[] reiURL = new URL[] {eineURL};
URLClassLoader clazLader = new URLClassLoader(reiURL);
c01 = clazLader.loadClass("test.SerObj");
return c01;
}
return super.resolveClass(desc);
}
};
SerObj obj = (SerObj) o.readObject();
The problem is in the last line. My current class version is placed in U:/MyJavaProj/bin/test/SerObj.class
My old class version is placed in U:/MyJavaProj/bin/test/oldversion/test/SerObj.class
In the last line I read the old class version but cast it to the new version 🙁
Has anyone some idea or maby any other aproach to add the versioning support for the serialization in Java?
You won’t be able to perform the cast in the last line, because it’s of a different class (which is not castable) – you might get a confusing message such as
test.SerObj is not an instance of test.SerObj.This arises from the fact that a class is essentially a
java.lang.Classinstance, which critically is unique within a given classloader. Even if you referenced the exact same*.classfile, an instance ofSerObjloaded by the default classloader is a different class from an instance ofSerObjloaded byclazLader. Neither class could be cast to the other one.Thus it is not going to be possible for the method to return
SerObjif you need to use multiple classloaders to deserialise. (Besides – how could it, when the two class files could have arbitrary differences?)One possible workaround is to define an interface that defines the behaviour which is fixed between versions of the class. If you set up the temporary classloader such that it can only load
SerObjfrom the oldversion class file, and that it has the right parent classloader, then your oldSerObjshould still implement the interface class as defined by the root classloader.In this was, both the new
SerObjand oldSerObjinstances would be castable to theSerObjIfaceinterface, and so you could declare your method to return that. And in fact if you want callers to deal with different yet similar classes (which is what different versions of “the same class” are), you should arguably be returning an interface for this anyway.