4

I have an enum JJJ that has 3 values: A, B, and C. In previous versions of my program it had one additional value: D. I'd like to be able to read in the serialized objects created by previous versions of the program, but an exception is thrown when it encounters a JJJ type variable with value 'D' in the serialized object. Optimally, I'd like to be able to intercept the deserialization process and tell it to just map D's to C's when it encounters them.

According to http://docs.oracle.com/javase/6/docs/platform/serialization/spec/serial-arch.html (Serialization of Enum Constants), it doesn't sound like there is a simple way to do this... I know that one approach would be to override readObject on the classes that contain member variables of type JJJ, but this would be difficult and painful due to the size and scope of the program (dozens of serializable classes have member vars of type JJJ and overriding readObject to handle the JJJ field means I'd have to manually handle all of the other fields as well).

I've also attempted to solve this by rolling my own subclass of ObjectInputStream, but unfortunately the enum deserialization bits I really need to get at and override to solve this are all private or package private...

Any suggestions?

BonusLord
  • 363
  • 1
  • 7
  • 3
    This is an example of why serialization is not suited for storing data long-term: the serialized files are coupled tightly to your source code. Use a standard, well-documented, extensible format for long-term storage of data instead of Java serialization. – Jesper Jan 05 '12 at 13:56
  • 1
    Can you re-add `D` and at least mark it as deprecated, for the time needed for a migration? Or will that break other code? – Lukas Eder Jan 05 '12 at 13:59
  • @Lukas - I'd like to keep D out of the enum; other parts of the program iterate over the values of this enum for various purposes and would need tweaking to support legacy unwanted values. – BonusLord Jan 05 '12 at 14:05
  • Serialize in a better format? Jokes aside, can't you use the old enum and just convert all Ds to Cs after you load? – Viruzzo Jan 05 '12 at 14:05
  • 1
    @Jesper - While Java serialization does have some drawbacks when being used for this purpose, it also provides a lot of benefits such as putting less burden on programmers to serialize data and not having as much boilerplate code to do things like routine object serialization. Though perhaps in the future we will be able to migrate to a more flexible framework... – BonusLord Jan 05 '12 at 14:10
  • @BonusLord: JAXB might be a good option for the future. It doesn't add much boilerplate code, too – Lukas Eder Jan 05 '12 at 14:13

2 Answers2

0

FYI, for any other poor souls out there trying to handle this case, this is the solution I ended up implementing:

I created a special interface for enums that looks like this:

public interface SerialCompatEnum {
    public String convertOldValue(String oldValue);
}

I then wrote a class that takes an raw serialized java object byte stream as input, processes it, and writes a modified version of the byte stream to an arbitrary output stream. The processing logic picks up any enum instances in the serialized byte stream and uses the convertOldValue method to replace the enum constant in the stream with an updated string that actually exists in the current code base. We run all of our serialized object through this processor first to prevent total failures caused by missing enum constants.

Unfortunately, not a very clean or easy solution. :/

BonusLord
  • 363
  • 1
  • 7
  • Any particular code example please? :) Recently was struggling with getting those byte stream from javax.ws.rs.core.Response and processing it before reading entity, but I definitely was missing something... finally implemented alternate version of API to avoid all the pain :) – RAM237 Jan 22 '21 at 13:30
  • 1
    The class I wrote that handles the actual byte stream modification is a ~1500 line mess that is basically a full implementation of a stream-based parser for the Java serialized objects grammar / format. It's not really portable in it's current form but I may try to rework it into a proper library someday. – BonusLord Mar 01 '21 at 14:43
0

I haven't actually tried this, but perhaps it could work:

  1. Create a new class "oldClass" with exactly the same fields as the old one.

  2. Extend ObjectInputStream in the way this post describes so that you deserialize in an instance of "oldClass".

  3. In that "oldClass" you have created, implement "readResolve()", so that you return a instance of the new version of your class, changing the missing fields as you like.

Community
  • 1
  • 1
Pablo
  • 3,655
  • 2
  • 30
  • 44
  • Unfortunately, you cannot implement 'readResolve()' on an enum (well you _can_ but it won't get called~) – BonusLord Jan 05 '12 at 14:36