如何反序列化对象,如果它被移动到另一个包或重命名?
考虑以下情况:
有一个序列化文件,由旧版本的应用程序创build。 不幸的是,这个包已经改变了,已经被序列化了。 现在我需要从这个文件加载信息到同一个类,但是位于不同的包中。 这个类已经定义了serialVersionUID
,并没有改变(即兼容)。
问题:是否有可能使用任何技巧从这个文件中加载新的类实例(除了将类复制到旧包,然后使用反序列化包装逻辑)? 有可能使用readResolve()
来移动/重命名类的恢复? 如果没有,请解释原因。
问题:是否有可能使用任何技巧从这个文件中加载新的类实例(除了将类复制到旧包,然后使用反序列化包装逻辑)?
我不认为你可以使用任何其他的“技巧”,至less不涉及序列化协议的部分重新实现。
有可能使用readResolve()来移动/重命名类的恢复? 如果没有,请解释原因。
不,因为反序列化机制会在更早的时候失败,在试图find被反序列化的类的阶段 – 无法知道另一个包中的类有它应该使用的readResolve()
方法。
有可能的:
class HackedObjectInputStream extends ObjectInputStream { public HackedObjectInputStream(InputStream in) throws IOException { super(in); } @Override protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); if (resultClassDescriptor.getName().equals("oldpackage.Clazz")) resultClassDescriptor = ObjectStreamClass.lookup(newpackage.Clazz.class); return resultClassDescriptor; } }
这还允许忽略serialVersionUIDs不匹配,或者如果字段结构发生更改,则可以反序列化类。
也许你最好的办法是重新创build旧的类(名称,包和序列号),读取序列化的forms,然后将数据复制到新对象的一个实例,并重新将其串行化。
如果你有很多这些序列化的对象,也许你可以写一个小脚本来做到这一点,所以“模式更改”一次完成。
另一个select是复活旧类并实现其readResolve方法以返回新类的实例(可能通过声明复制构造函数)。 就我个人而言,我想我会去模式更改脚本,然后删除旧的类为好。
如果您使用天鹅座hex编辑器,您可以手动更改包/类的名称。
如果新名称(总是包括包)具有相同的大小,则可以用新名称replace旧名称,但是如果大小已更改,则需要使用新的新长度更新名称前面的前两个字符。
右键单击标准数据types并更改为Big Endian。
长度是签字。
例如:
00 0E 70 61 63 6B 61 67 65 2E 53 61 6D 70 6C 65 . . package . S ample
package.Sample是如何写的。 00 0E表示14,字符数“package.Sample”。
如果我们想更改为newpackage.Sample,我们将该stringreplace为:
00 12 6E 65 77 70 61 63 6B 61 67 65 2E 53 61 6D 70 6C 65 . . newpackage . S ample
00 12表示18,字符数为“newpackage.Sample”。
当然你可以让修补程序自动更新它。
我不认为有可能做你想做的事情。
序列化文件的格式保持类名称。 详细来说它有下一个结构:
AC ED
协议版本号
对象数据
对象的类描述
类描述有下一个格式:
全class的名字
串行版本唯一ID(来自字段和方法签名的SHA1)
序列化选项
字段描述符
当你试图反序列化对象的序列化机制时,先比较类名(而你不通过这个步骤),然后它比较serialVersionUID的,只有在通过这2个步骤反序列化对象之后。