5 月 20 日更新:我应该提到有问题的对象确实设置了“serialVersionUID”(旧和新的值相同),但在调用 readObject() 之前序列化失败,但有以下异常:
Exception in thread "main" java.io.InvalidClassException: TestData; incompatible types for field number
我现在也在下面包含一个示例。
我正在使用一个大型应用程序,该应用程序将序列化对象(实现 Serializable,而不是 Exernalizable)从客户端发送到服务器。不幸的是,我们现在遇到了一个现有字段完全改变类型的情况,这破坏了序列化。
计划是先升级服务器端。从那里,我可以控制序列化对象的新版本,以及 ObjectInputStream 来读取来自客户端的(最初是旧的)对象。
我一开始想到在新版本中实现 readObject() ;但是,在尝试之后,我发现在调用该方法之前验证失败(由于类型不兼容)。
如果我继承 ObjectInputStream,我能完成我想要的吗?
更好的是,是否有任何第 3 方库可以执行任何类型的序列化“魔术”?如果有任何工具/库可以将序列化的对象流转换为类似 HashMaps 的数组...而不需要自己加载对象,那将是非常有趣的。我不确定是否可以这样做(将序列化对象转换为 HashMap 而不加载对象定义本身),但如果可能的话,我可以想象一个可以转换序列化对象(或对象流)的工具) 到新版本,例如,使用一组用于转换提示/规则等的属性...
感谢您的任何建议。
5 月 20 日更新- 下面的示例源 - TestData 中的字段“数字”从旧版本中的“int”更改为新版本中的“Long”。注意新版本的 TestData 中的 readObject() 不会被调用,因为在它到达之前会抛出异常:
线程“main”中的异常 java.io.InvalidClassException: TestData; 字段编号的不兼容类型
下面是源码。将其保存到文件夹,然后创建子文件夹“旧”和“新”。将 TestData 类的“旧”版本放在“旧”文件夹中,将新版本放在“新”文件夹中。将“WriteIt”和“ReadIt”类放在主(旧/新的父级)文件夹中。'cd' 到 'old' 文件夹,然后编译: javac -g -classpath . TestData.java
...然后在 'new' 文件夹中做同样的事情。'cd' 回到父文件夹,并编译 WriteIt/ReadIt:
javac -g -classpath .;old WriteIt.java
javac -g -classpath .;new ReadIt.java
然后运行:
java -classpath .;old WriteIt //Serialize old version of TestData to TestData_old.ser
java -classpath .;new ReadIt //Attempt to read back the old object using reference to new version
[旧版]TestData.java
import java.io.*;
public class TestData
implements java.io.Serializable
{
private static final long serialVersionUID = 2L;
public int number;
}
[新版本] TestData.java
import java.io.*;
public class TestData
implements java.io.Serializable
{
private static final long serialVersionUID = 2L;
public Long number; //Changed from int to Long
private void readObject(final ObjectInputStream ois)
throws IOException, ClassNotFoundException
{
System.out.println("[TestData NEW] readObject() called...");
/* This is where I would, in theory, figure out how to handle
* both the old and new type. But a serialization exception is
* thrown before this method is ever called.
*/
}
}
WriteIt.java - 将旧版本的 TestData 序列化为“TestData_old.ser”
import java.io.*;
public class WriteIt
{
public static void main(String args[])
throws Exception
{
TestData d = new TestData();
d.number = 2013;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("TestData_old.ser"));
oos.writeObject(d);
oos.close();
System.out.println("Done!");
}
}
ReadIt.java - 尝试将旧对象反序列化为新版本的 TestData。新版本中的 readObject() 没有被调用,因为事先有异常。
import java.io.*;
public class ReadIt
{
public static void main(String args[])
throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("TestData_old.ser"));
TestData d = (TestData)ois.readObject();
ois.close();
System.out.println("Number = " + d.number);
}
}