How to look at a serialized Java object?

I have an object from old Java code, and now I have changed the serialized object code. I want to be able to read both old files and new files. I need a branch operator in readObject to do something like:

if (next object is int -- just poking, it might be an Object) { // we know we are in version 1 } else { // read new version of object } 

what can be done?

+4
source share
4 answers

So basically the question arises: "How can we check with ObjectInputStream whether the next field is primitive or an object?" and the answer, as far as I can see, is: you cannot.

This means that the best solution I can see to maintain backward compatibility is to never remove the primitives from the original version, since useless information inflates the size a bit, but otherwise it's easy.

To add new fields, I see two ways:

  • Keep the message format and add only new objects at the end - you can easily distinguish different versions from the size of the message (or, more precisely, you will get an IOException when reading data v2 when you get object v1). This is much simpler and generally preferable.

  • You can change objects from v1 to v2, and then do a simple instanceof check. If you want to add primitives, you need to save their shell versions (i.e. Integer , etc.). It may save you a few bytes, but the Java serialization protocol has never been effective to start with, so it's really not complicated.

+1
source
 if (object instanceof Integer) { ... Do stuff } else { ... Do other stuff } 

EDITOR: I suppose I should expand this. You can check the types of objects using instanceof , but I'm not sure I can work with primitives like int or char .

+2
source

The easiest way to do this is to keep the old member variables with their old types and add new member variables for the new types. In addition, you must keep the serialVersionUID of this class the same. then your readObject () implementation can perform any necessary manipulations to convert old data to new data.

Original Object:

 public class MyObject { private static final long serialVersionUID = 1234L; private int _someVal; } 

A new version:

 public class MyObject { private static final long serialVersionUID = 1234L; private int _someVal; //obsolete private String _newSomeVal; private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); if(_someVal != 0) { // translate _someVal to _newSomeVal } } } 

I believe there are more complex options, as well as custom ObjectStreamField[] serialPersistentFields , ObjectInputStream.GetField and ObjectOutputStream.PutField .

+1
source

ObjectInputStream load and instantiate the desired class.

 object = ois.readObject(); if (object instanceof YourNewShiny ){ // new style object } else if (object instanceof YourOldBusted ){ // convert YourOldBusted to YourNewShiny } else { throw new ToyOutOfPram(); } 

This is great if you have a new class, but if you changed your class in an incompatible way, so that ObjectInputStream cannot deserialize old versions of the class in a new form. If so, you are very full.

Parameters:

  • Discard your changes and make them compatible, for example, add serialVersionId, do not change the order of fields, add only a new field and do not allow non-zero restrictions.
  • Using the old version of the code, read the serialized data, convert it to some intermediate form (xml, csv, etc.), and then import this data into a new class definition and serialize
  • manually repeat ObjectInputStream to determine the type of your class (you can use serialVersionId for a hard type)

Only the first seems like a good idea to me.

0
source

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


All Articles