我想在序列化数据流中用“com.newPackage.className”替换字符串“com.oldpackage.className”。从DB读取此序列化数据,并在替换字符串后更新。
我在做同样的事情时遇到了一些问题。如果您已经猜到了,这是重构练习的一部分。是否有任何库可以帮助我操作序列化数据?如果您还可以对任何预防措施或警告发表评论,那将会非常有帮助。
非常感谢, 克里斯。 P.S:旧类和新类都没有将serialversion ID声明为其字段的一部分。
答案 0 :(得分:5)
我不知道如何做你正在尝试的但我可以建议你“合法”的解决方案。在类路径中实现包含旧包和新包的转换器,并执行以下操作:从DB读取数据,使用旧包对其进行反序列化,在Java中将旧实例转换为new,然后再将新数据存储在DB中。
此解决方案需要执行一些脏工作,但它是安全的。此外,结果可能在将来重复使用。但我祝你好运,找到只替换序列化数据中的类名的解决方案。
答案 1 :(得分:4)
为了准确回答Tom Hawtin的回复,我设法使用以下代码实现它:
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;
}
};
}
请注意,您还需要覆盖readClassDescriptor()。它适用于标准类型和数组,甚至可以更改类名而不仅仅是包名。只是做:
InputStream in = new ByteArrayInputStream(classBytes);
ObjectInputStream ois = getSwapOIS( in,
"com.oldpackage.className",
"com.newpackage.newClassName");
Object myObject= ois.readObject();
答案 2 :(得分:2)
IIRC您可以(具有足够的权限)覆盖ObjectInputStream.resolveClass
(和resolveProxyClass
)以返回具有不同包名称的Class
。虽然,IIRC,你不能改变简单的类名。
答案 3 :(得分:1)
如果要在数据库中以字符串形式存储要重构的数据,一个选项是使用SQL来简单地更新该数据,例如(在伪MySQL中)
update mydata
set serialized_data =
replace(serialized_data, "com.oldpackage", "com.newpackage")
当然,在尝试从数据库重新读取这些数据之前,您会重构Java代码。
答案 4 :(得分:1)
我能否提出替代方案。当用于长期存储时,Java序列化格式存在一些重要问题。
由于您正在重构并且很可能不得不以某种方式对所有存储的数据进行反序列化,因此您可能希望将Google Protocol Buffers视为替代序列化格式。
可能需要更多工作,但它专为长期存储和版本设计而设计。
祝你好运