Make Java runtime ignore serialVersionUIDs?
Solution 1
If you have access to the code base, you could use the SerialVer task for Ant to insert and to modify the serialVersionUID
in the source code of a serializable class and fix the problem once for all.
If you can't, or if this is not an option (e.g. if you have already serialized some objects that you need to deserialize), one solution would be to extend ObjectInputStream
. Augment its behavior to compare the serialVersionUID
of the stream descriptor with the serialVersionUID
of the class in the local JVM that this descriptor represents and to use the local class descriptor in case of mismatch. Then, just use this custom class for the deserialization. Something like this (credits to this message):
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DecompressibleInputStream extends ObjectInputStream {
private static Logger logger = LoggerFactory.getLogger(DecompressibleInputStream.class);
public DecompressibleInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
Class localClass; // the class in the local JVM that this descriptor represents.
try {
localClass = Class.forName(resultClassDescriptor.getName());
} catch (ClassNotFoundException e) {
logger.error("No local class for " + resultClassDescriptor.getName(), e);
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());
logger.error("Potentially Fatal Deserialization Operation.", e);
resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
}
}
return resultClassDescriptor;
}
}
Solution 2
How impractical is this to fix ? If you have the source and can rebuild, can you not just run a script over the entire codebase to insert a
private long serialVersionUID = 1L;
everywhere ?
Solution 3
Use CGLIB to insert them into the binary classes?
Solution 4
The serialization errors at runtime tell you explicitly what the ID is expected to be. Just change your classes to declare these as the ID and everything will be OK. This does involve you making changes but I don't believe that this can be avoided
kpozin
Updated on June 13, 2022Comments
-
kpozin almost 2 years
I have to work with a large number of compiled Java classes which didn't explicitly specify a serialVersionUID. Because their UIDs were arbitrarily generated by the compiler, many of the classes which need to be serialized and deserialized end up causing exceptions, even though the actual class definitions match up. (This is all expected behavior, of course.)
It is impractical for me to go back and fix all of this 3rd-party code.
Therefore, my question is: Is there any way to make the Java runtime ignore differences in serialVersionUIDs, and only fail to deserialize when there are actual differences in structure?
-
Devanshu Mevada over 14 yearsThis is a good idea but this is not enough. The problem is not to add a serialVersionUID, the problem is to add the same serialVersionUID as in the serialized version.
-
Devanshu Mevada over 14 yearsGood idea but... what value do you insert exactly? You need to read the serialVersionUID of the serialized version. That's the tricky part.
-
Artem over 14 yearsOh, whoops. Yes, you'd need to know what's out there.
-
Brian Agnew almost 9 yearsIs the latter message link broken ? (after six years, I'm not hugely surprised, mind)
-
nserror almost 8 yearsMany thanks. This snippet did in fact save the day for me as well.
-
nyholku about 6 yearsA rea life saver! Thanks!
-
Pecana about 4 yearsIt is quite old, but how to use this then ? How to call it ?
-
OrangeDog over 2 yearsThis can be greatly simplified to just
return ObjectStreamClass.lookupAny(Class.forName(super.readClassDescriptor().getName()))
. None of the checks/catches are needed. -
MG Developer almost 2 yearsI am sorry but you need to really understand the ask before proposing some non workable solution. He said the objects are already serialized and he does not own the third party libraries. I am in the same boat, it would be been nice if Java had a flag to ignore UID.