Replacing class name in serialized data
I 开发者_StackOverflow中文版want to replace the String "com.oldpackage.className" with "com.newPackage.className" in a stream of serialized data. This serialized data is read from the DB and updated after replacing the string.
I am facing some problems while doing the same. If you already guessed it, this is part of a refactoring exercise. Are there any libraries that would help me in manipulating the serialized data? If you can also please comment on any precautions or caveats, it would be of great help.
Thanks a lot, Chris. P.S: Both the old class and the new class do not declare a serialversion ID as part of its fields.
I do not know how to do what your are trying but I can suggest you "legal" solution. Implement convertor that has both old and new packages in classpath and does the following: reads data from DB, de-serializes it using the old package, converts old instances to new in java and then stores new data in DB again.
This solution requires performing some dirty job but it is safe. Moreover the results may be reused in future. But I wish you good luck to find solution that just replaces class name in serialized data.
To precise Tom Hawtin's reply, I have managed to implement it using the following code:
public static ObjectInputStream getSwapOIS( InputStream in ,String fromClass,String toClass)
throws IOException ,ClassNotFoundException {
final String from="^"+fromClass,fromArray="^\\[L"+fromClass,toArray="[L"+toClass;
return new ObjectInputStream(in) {
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
String name = desc.getName().replaceFirst(from, toClass);
name = name.replaceFirst(fromArray, toArray);
return Class.forName(name);
}
protected ObjectStreamClass readClassDescriptor()
throws IOException, ClassNotFoundException
{
ObjectStreamClass cd = super.readClassDescriptor();
String name = cd.getName().replaceFirst(from, toClass);
name = name.replaceFirst(fromArray, toArray);
if(!name.equals(cd.getName())) {
cd = ObjectStreamClass.lookup(Class.forName(name));
}
return cd;
}
};
}
Note that you also need to override readClassDescriptor(). It works for both standard types and arrays and you can even change the class name not just the package name. Just do:
InputStream in = new ByteArrayInputStream(classBytes);
ObjectInputStream ois = getSwapOIS( in,
"com.oldpackage.className",
"com.newpackage.newClassName");
Object myObject= ois.readObject();
IIRC you can (with sufficient permissions) override ObjectInputStream.resolveClass
(and resolveProxyClass
) to return a Class
with a different package names. Although, IIRC, you cannot change the simple class name.
Can i suguest an alternative tack. The java serialisation format has some significant issues when being used for long term storage.
Since you are refactoring and are most likely going to have to deserialise all the stored data one way or another, you may want to look at Google Protocol Buffers as an alternative serialisation format.
It might take a bit more work but it is designed specifically for long term storage and versioning.
Good luck
If the data-to-be-refactored is being stored - in string form - in a database, one option is to use SQL to simply update that data, e.g., (in pseudo MySQL)
update mydata
set serialized_data =
replace(serialized_data, "com.oldpackage", "com.newpackage")
you would, of course, refactor your Java code before attempting to re-read this data from the DB.
精彩评论