How to deserialize an object persisted in a db now when the object has different serialVersionUID

后端 未结 6 1924
太阳男子
太阳男子 2020-12-14 04:34

My client has an oracle data base and an object was persisted as a blob field via objOutStream.writeObject, the object now has a different serialVersionUID (eve

相关标签:
6条回答
  • 2020-12-14 05:00

    If you've got multiple versions of the class stored in the database, it might be pretty tricky to deserialize and upgrade them all to a consistent serialization format in a single pass.

    If possible, you might alter the table with a column to flag whether the serialized object has been processed yet. Then make passes over the table for each serialVersionUID, where you try to process any objects that haven't been dealt with yet. You can catch the InvalidClassException and go on to the next record if your updater encounters a serialized object that it doesn't handle, making a note of the version number so that you can make another pass.

    This is a little tedious, but very simple.

    Java serialization has some very nice features to support evolution of classes. However, you have to be aware of what you are doing. It may be that all of the objects actually have the same data, but no attention was paid to maintaining the version ID.

    You could continue to use serialization once you get all of the objects updated to the same version. Just be careful as you add new fields to the class that they make sense with their default values (booleans are false, Objects are null, ints are zero, etc).

    0 讨论(0)
  • 2020-12-14 05:02

    you can find the serial UID in HEX format, if you store serialized data in db, you can edit and replace the old UID with new serial UID in HEX format

    0 讨论(0)
  • 2020-12-14 05:04

    Bit of a hack perhaps, but might be helpful to someone: I had a similar problem, which I solved by duplicating the offending class and settings the new class UID to 0L (for example). Then in the code which does the serialisation I copied the original object into the new object and serialised out. Then you can update your code, and deserialisation code, to use the new class in place of the old. This works perfectly, although you are stuck with the new class name. You can repeat the process, however, to recover your old class name. At the end of it, you have a fixed UID of your choosing. Tip I learned the hard way: always set your own UID !

    0 讨论(0)
  • 2020-12-14 05:06

    Jorge I found one solution on http://forums.sun.com/thread.jspa?threadID=518416 which works.

    Create the below class in your project. Whereever you creating object of ObjectInputStream, use DecompressibleInputStream instead and it deserializes the old object with the new version Id class.

    public class DecompressibleInputStream extends ObjectInputStream {
    
        public DecompressibleInputStream(InputStream in) throws IOException {
            super(in);
        }
    
    
        protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
            ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
            Class localClass = Class.forName(resultClassDescriptor.getName()); // the class in the local JVM that this descriptor represents.
            if (localClass == null) {
                System.out.println("No local class for " + resultClassDescriptor.getName());
                return resultClassDescriptor;
            }
            ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
            if (localClassDescriptor != null) { // only if class implements serializable
                final long localSUID = localClassDescriptor.getSerialVersionUID();
                final long streamSUID = resultClassDescriptor.getSerialVersionUID();
                if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
                    final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: ");
                    s.append("local serialVersionUID = ").append(localSUID);
                    s.append(" stream serialVersionUID = ").append(streamSUID);
                    Exception e = new InvalidClassException(s.toString());
                    System.out.println("Potentially Fatal Deserialization Operation. " + e);
                    resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
                }
            }
            return resultClassDescriptor;
        }
    }
    
    0 讨论(0)
  • 2020-12-14 05:09

    I may be missing something, but it sounds like you're trying to do something more complicated than necessary. What happens if:

    (a) you take the current class definition (i.e. the source code) and hard-code its serial UID to the old one (or one of the old ones), then use that class definition to deserialise the serialised instances?

    (b) in the byte stream you're reading, you replace the old serial UIDs with the new one before wrapping the ObjectInputStream around them?

    OK, just to clarify (b). So for example, if I have a little class like this:

      public static class MyClass implements Serializable {
        static final long serialVersionUID = 0x1122334455667788L;
        private int myField = 0xff;
      }
    

    then when the data is serialised, it looks something like this:

    ACED000573720011746573742E546573 ’..sr..test.Tes
    74244D79436C61737311223344556677 t$MyClass."3DUfw
    880200014900076D794669656C647870 ?...I..myFieldxp
    000000FF ...ÿ
    

    Each line is 16 bytes, and each byte is 2 hex digits. If you look carefully, on the second line, 9 bytes (18 digits) in, you'll see the serial version ID starts (1122...). So in our data here (yours will differ slightly), the offset of the serial version ID is 16 + 9 = 25 (or 0x19 in hex). So before I start deserialising, if I want to change this serial version ID to something else, then I need to write my new number at offset 25:

    byte[] bytes = ... serialised data ...
    ByteBuffer bb = ByteBuffer.wrap(bytes);
    bb.putLong(25, newSerialVersionUID);
    

    then I just proceed as normal:

    ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytes));
    MyClass obj = (MyClass) oin.readObject();
    
    0 讨论(0)
  • 2020-12-14 05:19

    You should be able to hack around the issue by overriding ObjectInputStream.readClassDescriptor.

    Using XMLEncoder wont actually help with version migration as the compatibility rules are much the same. Really what should probably be doing is persisting the object in a relational form with the help of an ORM tool.

    Probably the different serialVersionUIDs happened due to different synthetic members being generated by javac. Head the warnings and put serialVersionUID in.

    0 讨论(0)
提交回复
热议问题