I have read various blogs about Serialization and the use of serialVersionUID. Most of them mention using it to maintain the state of a serializable class.
The scenario I have is;
I know the old serialVersionUID and the new serialVersionUID.
On reading in an object with the old serialVersionUID I want to manipulate the data so it fits the new version, I only want to bother to do this if the object I am reading in is of the old type.
This seems like something that should be very straight forwards!
Is there a way to get hold of the serialVersionUID as the object is read in?
The InvalidClassException is thrown before the readObject method in the serialized class is invoked so I can't access it there.
The only hint I have found is to override the ObjectInputStream so that readClassDescriptor() is available although this seems a heavy weight solution to what must be a common problem!
All help is gratefully received!
M
You should keep the same serialVersionUID
. The serialised fields don't have to match those of the class itself. Use ObjectInputStream.readFields
and define a serialPersistentFields
(although do make sure you spell that correctly).
I am going to present a few possible ways to support older versions of a class/interface in serialization:
Use Migrators
In such a case you need the following in your Java project:
@Deprecated
Let me say from the beginning that it's not always possible to work with objects of older versions (see the Oracle documentation on versioning of serializable objects). For the sake of simplicity, let us assume that while upgrading, your classes always implement the interface IEntity
that you have defined.
public interface IEntity extends Serializable {
// your method definitions
}
And assume that you initially work with the class:
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
If you need to upgrade the class Entity
into a new implementation with a new serialVersionUID, then first annotate it as @Deprecated
but don't rename it and don't move it to another package :
@Deprecated
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
Now create your new implementation with a new serialVersionUID (important) and an additional constructor as follows...
public class Entity_new implements IEntity {
private static final long serialVersionUID = 5555558L;
public Entity_new(IEntity){
// Create a new instance of Entity_new copying the given IEntity
}
}
Of course the most critical part of the whole procedure is how you implement the above constructor. If you have serialized some objects of the old type Entity
as binary files (using for example ObjectOutputStream
) and you have now migrated to Entity_new
you can parse them as instances of Entity
and then convert them into instances of Entity_new
. Here is an example:
public class Migrator {
private final IEntity entity;
private Class<? extends IEntity> newestClass = Entity_new.class;
public Migrator(final IEntity entity){
this.entity = entity;
}
public Migrator setNewestClass(Class<? extends IEntity> clazz){
this.newestClass = clazz;
return this;
}
public IEntity migrate() throws Exception {
Constructor<? extends IEntity> constr =
newestClass.getConstructor(IEntity.class);
return constr.newInstance(this.entity);
}
}
There are of course other alternative that don't require that particular constructor or use java reflection. There are lots of other design approaches one can choose. Note also that exception handling and checks for null objects have been completely omitted for the sake of simplicity in the above code.
Design a generic serializable interface
If applicable, you can try in the first place to design an interface for your class that is not likely to change in the future. If your class needs to store a set of properties that is very likely to be modified, consider using a Map<String, Object>
for this purpose, where String
refers to the property name/identifier and Object
is the corresponding value.
Customize readObject and writeObject
There is yet another way to provide support for older version which I will mention for completeness, but is not one I would choose. You can implement private void readObject(ObjectInputStream in)
and private void writeObject(ObjectOutputStream out)
in a way that it accommodates both the current and all previous versions of your class/interface. I am not sure whether something like this is always feasible and sustainable and you may end up having a very messy and lengthy implementation of these methods.
Alternative serialization techniques
This doesn't answer the OP's question, but I reckon it is worth bringing it up. You may consider serializing your object in some ASCII format such as JSON, YAML or XML. In such a case, unless you vehemently redesign your serializable interface, extensibility comes out of the box. BSON (binary JSON) is a good choice if you are looking for an extensible binary protocol. Maybe this is the best way to go to provide portability of your objects across software that may not be implemented in Java.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With