Home > Programming > Refactor Serializable Java Class

Refactor Serializable Java Class

Today I renamed and moved a class in a Java program. It was a Serializable class containing some preferences. As we are getting rid of it I renamed it from Preferences to LegacyPreferences, with the intention of starting a new Preferences class using a regular config file instead of serialization. Bad idea. Java refused to load serialized data into the new class for two reasons: one obvious and expected and one seriously braindamaged in true Java fashion.

The first problem was that the deserialization class could no longer find the class to deserialize to. No wonder since it was in a new package and had a new name. No problem. I subclassed ObjectInputStream and overrode resolveClass(ObjectStreamClass desc) to properly load the class from its new name and location.

That’s when Java freaked out with the following piece of brilliance:

local class name incompatible with stream class name X

Well, duh. Of course the name is different, that’s the point of a refactoring. What exactly were they trying to accomplish when they added this name check? The class has already been found, its interface is an exact match. The name of the class is irrelevant at this point.

I proceeded by overriding readClassDescriptor() with the intention to simply replace the name of the class with the new name when reading the stream. Java would never even realize the class used to have another name. But no: that method must return a java.io.ObjectStreamClass object, which you can’t instantiate or override unless you’re in the java.io package. And if you just grab the super.readClassDescriptor() instance you aren’t allowed to make any changes to it.

At this point I considered my favorite solution to Java damage. I’d like to call it “corrective Java surgery” – use reflection to force your way into the class and fix it. This is a very gratifying solution since you can undo the mistakes in Java and set things truly right. The drawback of course is that different versions of Java may require different corrective code.

That wasn’t necessary in this case since reading the source code of ObjectInputStream revealed a static method for creating ObjectStreamClasses: ObjectStreamClass.lookup. With that tool things fell into place very simply like so:

@Override
protected java.io.ObjectStreamClass readClassDescriptor() throws IOException ,ClassNotFoundException {
	ObjectStreamClass desc = super.readClassDescriptor();
	if (desc.getName().equals("com.trueship.base.Preferences")) {
		return ObjectStreamClass.lookup(LegacyPreferences.class);
	}
	return desc;
};

Hope that’ll help anyone else looking to rename and deserialize classes in Java.

Be Sociable, Share!
Categories: Programming Tags:
  1. Leo
    May 25th, 2011 at 16:05 | #1

    Thanks, that helped me a lot! I had to idea how to deal with this issue.

  2. September 18th, 2012 at 21:41 | #3

    I’ve found a draw back with this solution. As you point out you can’t just change the name, you have to get a new instance of java.io.ObjectStreamClass. If you’ve made an change to your serializable class, adding a field for instance, this is part of the new ObjectStreamClass instance and causes the deserialization to fails as its expecting the new field to be there. The ObjectStreamClass returned from super.readClassDescriptor() contains a description of the class at the point it was serialized, allowing for new member to dealt with appropriately.

    Did you notice this issue at all or were you dealing with a stable class?

  3. January 26th, 2013 at 13:11 | #4

    @CodeBuddy
    Unfortunately it’s been a few years so I don’t remember, and we only changed the name without adding fields.

  1. No trackbacks yet.