Zephyr
Zephyr

Reputation: 10253

How to deserialize object to new object with the same name?

I have an application that previously stored some data by serializing the model object (QuickNote). This was then loaded by deserialization.

However, I am updating the application to store the data by XML instead and I need to be able to import the old, serialized object for conversion. The new object, which does not implement Serializable, also has the same name though.(QuickNote).

I am trying to deserialize the old QuickNote and store it into the new one using the follow code:

FileInputStream fileIn = new FileInputStream("data/quicknotes_user.dat");
ObjectInputStream in = new ObjectInputStream(fileIn);
List<OldQuickNote> newList = (List<OldQuickNote>) in.readObject();

Since the class name for the old serialized file is QuickNote, I get the error java.io.InvalidClassException: model.QuickNote; class invalid for deserialization due to the new class having the same name and not implementing Serializable.

Is there a way to deserialize this QuickNote object into an OldQuickNote object? Both QuickNote and OldQuickNote are identical except that QuickNote no longer implements Serializable but OldQuickNote does.

I did try to search for answers, but to be honest, I am not even sure how to search for this as I obviously made a poor design decision when electing to serialize the QuickNote object in the first place.

Caveats:

  1. I cannot change the object type (class name) for the serialized object as it is now residing on the user's systems.
  2. The new QuickNote object cannot be changed to implement Serializable because it is annotated and used for JAXB marshalling.

Upvotes: 0

Views: 2373

Answers (2)

Zephyr
Zephyr

Reputation: 10253

This was answered in the comments but the one with the answer didn't post it as such.

I ended up moving the old class to a different package and converted the class when the user ran the program again.

All I had to do then was wait for my users to all update the the new version and deleted the old class.

Thank you!

Upvotes: 0

Roman Puchkovskiy
Roman Puchkovskiy

Reputation: 11865

One option is mentioned in the comments: force a migration of data to XML and then get rid of the old class.

Another option is to try to parse it without the usage of Java Serialization machinery. There are some tools that parse java serialized data like https://github.com/gagern/nodeJavaDeserialization I could not find any library in java though, but, if you only have one class data to parse, the task to parse it does not seem so scary. The documentation on the format is available: http://docs.oracle.com/javase/6/docs/platform/serialization/spec/serialTOC.html , and ObjectOutputStream/ObjectInputStream source too.

And one more option would be to play with the serialized data before deserializing. Imagine that you rename your existing QuickNote to QuickNot2 (ugly, but will be useful later) and create your new class as QuickNote. Then, before deserializing the QuickNot2, you change that e to 2 in the serialized bytes representation. Here is a proof of concept:

public class Data1 implements Serializable {
    private final static long serialVersionUID = 123L;

    private final String name;

    public Data1(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Data2 implements Serializable {
    private final static long serialVersionUID = 123L;

    private final String name;

    public Data2(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

And

Data1 data1 = new Data1("hello");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream dos = new ObjectOutputStream(baos);
dos.writeObject(data1);
dos.close();
byte[] serialized = baos.toByteArray();

serialized[12] = '2';

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serialized));
Data2 data2 = (Data2) ois.readObject();
System.out.println(data2.getName());

This prints 'hello'.

As you can see, here I serialize via one class and deserialize via another one by changing the name of the class in the serialized form (byte with index 12 is changed, it's where '1' is stored).

For this to work, the following must hold:

  1. Both classes (the old QuickNote and that QuickNot2 must have the same structure
  2. They both must have the same serialVersionUID, so if it is not written in your code explicitly, you'll need to find it out and specify it in your code. To find it out, you can just set some arbitrary value in your QuickNot2 class and see the value in the error message which is produced by deserializer.

QuickNot2 is suggested to leave class name length unchanged. It seems possible to change the class name fully, with its length, I just did not check this.

Upvotes: 2

Related Questions