mik*_*mky 9 java serialization
更新5月20日:我应该提到有问题的对象确实设置了"serialVersionUID"(旧的和新的都是相同的值),但是在调用readObject()之前序列化失败,但有以下异常:
Exception in thread "main" java.io.InvalidClassException: TestData; incompatible types for field number
我现在还在下面列举一个例子.
我正在使用一个大型应用程序,它从客户端到服务器发送序列化对象(实现Serializable,而不是Exernalizable).不幸的是,我们现在的情况是现有字段完全改变了类型,这打破了序列化.
计划是首先升级服务器端.从那里,我可以控制序列化对象的新版本,以及ObjectInputStream来读取来自客户端的(最初的旧)对象.
我起初想过在新版本中实现readObject(); 然而,在尝试之后,我发现在调用方法之前验证失败(由于不兼容的类型).
如果我是ObjectInputStream的子类,我可以完成我想要的吗?
更好的是,是否有任何第三方库可以进行任何类型的"魔术"?如果有任何工具/库可以将序列化的对象流转换为类似HashMaps的数组,那将非常有趣......无需自己加载对象.我不确定是否可以这样做(将序列化对象转换为HashMap而不加载对象定义本身),但如果可能的话,我可以设想一个可以转换序列化对象(或对象流)的工具)到一个新版本,使用一组属性转换提示/规则等...
谢谢你的任何建议.
5月20日更新 - 下面的示例源 - TestData中的字段'number'从旧版本中的'int'更改为新版本中的'Long'.注意在新版本的TestData中没有调用readObject(),因为在它到达之前会抛出异常:
线程"main"中的异常java.io.InvalidClassException:TestData; 字段编号不兼容的类型
以下是来源.将其保存到文件夹,然后创建子文件夹"旧"和"新".将TestData类的"旧"版本放在"old"文件夹中,将新版本放在"new"中.将"WriteIt"和"ReadIt"类放在main(旧/新的)父文件夹中.'cd'到'old'文件夹,并使用:javac -g -classpath . TestData.java
... 编译, 然后在'new'文件夹中执行相同的操作.'cd'回到父文件夹,并编译WriteIt/ReadIt:
javac -g -classpath .;old WriteIt.java
javac -g -classpath .;new ReadIt.java
Run Code Online (Sandbox Code Playgroud)
然后运行:
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
Run Code Online (Sandbox Code Playgroud)
[旧版本] TestData.java
import java.io.*;
public class TestData
implements java.io.Serializable
{
private static final long serialVersionUID = 2L;
public int number;
}
Run Code Online (Sandbox Code Playgroud)
[新版本] 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.
*/
}
}
Run Code Online (Sandbox Code Playgroud)
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!");
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
您的直接问题似乎是您没有为您的类修复serialVersionUID字符串.如果不这样做,对象序列化和反序列化代码将根据发送和接收的类型的表示结构生成UID.如果更改一端的类型而不更改另一端的类型,则UID不匹配.如果您修复了UID,那么阅读器代码将通过UID检查,您的readObject方法将有机会"解决"序列化数据的差异.
除此之外,我的建议是:
尝试同时更改所有客户端和服务器,以便您不需要使用readObject/writeObject hacks
如果这是不实际的,试图移开那里是版本可能的匹配,而你的系统的发展在任何使用Java序列化对象.使用XML,JSON或对"协议"或"架构"更改不太敏感的其他内容.
如果这不实用,请修改您的客户端/服务器协议.
我怀疑你会通过继承ObjectStream类获得任何牵引力.(看看OpenJDK代码库中的代码......)
| 归档时间: |
|
| 查看次数: |
5689 次 |
| 最近记录: |