Java Serializable Class Update

I read various blogs about Serializing and using serialVersionUID. Most of them mention using this to maintain the state of a serializable class.

The script I have is:

I know the old serialVersionUID and the new serialVersionUID.

When reading in an object with the old serialVersionUID, I want to manipulate the data to fit the new version, I only want to work hard if the object I'm reading has the old type.

It looks like it should be very straight forward!

Is there a way to get serialVersionUID when reading an object?

An InvalidClassException is thrown before the readObject method is called in the serialized class, so I cannot access it.

The only hint I found is to override ObjectInputStream so that readClassDescriptor () is available, although this seems like a heavy weighted solution for what should be a common problem!

All help is gratefully received!

M

+4
source share
3 answers

I will introduce several possible ways to support old versions of a class / interface in serialization:

Use Migars

In this case, you will need the following in your Java project:

  • The immutable interface that unites all these classes
  • Old implementation (s) of this interface annotated as @Deprecated
  • New implementation of your interface
  • The Migrator class that helps you convert an obsolete object to a new one.

Let me say from the very beginning that it is not always possible to work with objects of older versions (see Oracle documentation on version control of serializable objects ). For simplicity, suppose that when upgrading, your classes always implement the IEntity interface that you defined.

 public interface IEntity extends Serializable { // your method definitions } 

And suppose you are initially working with a class:

 public class Entity implements IEntity { private static final long serialVersionUID = 123456789L; // fields and methods } 

If you need to update the Entity class in a new implementation with the new serialVersionUID, first annotate it as @Deprecated , but do not rename it and do not move it to another package:

 @Deprecated public class Entity implements IEntity { private static final long serialVersionUID = 123456789L; // fields and methods } 

Now create a 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 important part of the whole procedure is how you implement the above constructor. If you serialized some objects of the old Entity type as binary files (using, for example, ObjectOutputStream ), and now you switched to Entity_new , you can analyze them as Entity instances and then convert them to Entity_new instances. 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 is, of course, another alternative that does not require this particular constructor or uses java reflection . There are many other design approaches you can choose. Also note that exception handling and checking for null objects have been completely omitted for simplicity in the code above.

Development of a universal serializable interface

If applicable, you can try first of all to develop an interface for your class, which is unlikely to change in the future. If your class needs to save a set of properties that are likely to change, consider using Map<String, Object> for this purpose, where String refers to the name / identifier of the property and Object refers to the corresponding value.

Configure readObject and writeObject

There is another way to provide support for the older version, which I will talk about for completeness, but not the one I would choose. You can implement private void readObject(ObjectInputStream in) and private void writeObject(ObjectOutputStream out) so that it contains both the current and all previous versions of your class / interface. I'm not sure that something like this is always doable and sustainable, and you can end up having a very dirty and lengthy implementation of these methods.

Alternative serialization methods

This does not answer the OP question, but I think it is worth raising it. You might consider serializing your object in some ASCII format, such as JSON, YAML, or XML. In this case, if you are not decisively redesigning your serializable interface, extensibility fails. BSON (binary JSON) is a good choice if you are looking for an extensible binary protocol. Perhaps this is the best way to ensure the portability of your objects through software that cannot be implemented in Java.

+12
source

You must leave the same serialVersionUID . Serialized fields must not match the fields of the class itself. Use ObjectInputStream.readFields and define serialPersistentFields (although make sure you pronounce it correctly).

+6
source

This is not an easy task. if your code supports both types of classes, the best way to deal with this is to not change the serialVersionUID, but instead determine whether the data is new or old, and read the data accordingly.

if you want to do a one-time update of old data to new, you need to configure some cool juggling, where the old class and the new class are available for the process (for example, separate class loaders). you need to read the data using the old class, copy to the new one and rewrite it. this is definitely not the best way to do something.

In short, changing serialVersionUID is not a way to maintain state, it is a way to indicate incompatibility (i.e. a situation where rescue is the only solution).

+1
source

Source: https://habr.com/ru/post/1339835/


All Articles