Java 反序列化包含一些不可反序列化对象的数组(回收反序列化部分) 背景
我试图以这样的方式编写对象反序列化:如果一个对象数组包含一些(由于代码更改)无法反序列化的对象,那么数组中的那些引用将变为null,而不是抛出异常;允许打捞该物体的剩余部分 我试过的 我尝试使用自定义序列化/反序列化,希望能够捕获异常并应用自定义的“makeitnull”逻辑。下面包含了这方面的代码。然而,我似乎能够捕捉到异常的第一点是,一旦整个数组反序列化失败Java 反序列化包含一些不可反序列化对象的数组(回收反序列化部分) 背景,java,arrays,serialization,Java,Arrays,Serialization,我试图以这样的方式编写对象反序列化:如果一个对象数组包含一些(由于代码更改)无法反序列化的对象,那么数组中的那些引用将变为null,而不是抛出异常;允许打捞该物体的剩余部分 我试过的 我尝试使用自定义序列化/反序列化,希望能够捕获异常并应用自定义的“makeitnull”逻辑。下面包含了这方面的代码。然而,我似乎能够捕捉到异常的第一点是,一旦整个数组反序列化失败 public class AppleHolder implements Serializable{ Apple[] apple
public class AppleHolder implements Serializable{
Apple[] apples=new Apple[5];
double otherData=15;
public AppleHolder(){
Apple goodApple=new Apple("GoodApple","tastyGood");
BadApple badApple=new BadApple("BadApple","tastyBad");
apples[0]=goodApple;
apples[1]=goodApple; // multiple references to same object intentional
apples[2]=goodApple;
apples[3]=badApple;
apples[4]=badApple;
}
private void writeObject(ObjectOutputStream o)
throws IOException {
o.writeObject(apples);
o.writeObject(otherData);
}
private void readObject(ObjectInputStream o)
throws IOException, ClassNotFoundException {
apples = (Apple[]) o.readObject();
otherData = (double) o.readObject();
}
public static void main(String[] args)
throws Exception {
/*
* (1) First run serialize()
* (2) Change the badApple's serialVersionUID to 2
* (3) Run deSerialize(()
*/
serialize();
//deSerialize();
}
public static void serialize() throws Exception{
AppleHolder testWrite = new AppleHolder();
FileOutputStream fos = new FileOutputStream("testfile");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(testWrite);
oos.flush();
oos.close();
}
public static void deSerialize() throws Exception{
AppleHolder testRead;
FileInputStream fis = new FileInputStream("testfile");
ObjectInputStream ois = new ObjectInputStream(fis);
testRead = (AppleHolder) ois.readObject();
ois.close();
System.out.println("--Read object--");
System.out.println("propertyOne: " + testRead.apples[0].getPropertyOne());
}
}
public class Apple implements Serializable {
private String propertyOne;
private String propertyTwo;
public Apple(String propertyOne, String propertyTwo) {
this.propertyOne = propertyOne;
this.propertyTwo = propertyTwo;
validate();
}
private void writeObject(ObjectOutputStream o)
throws IOException {
o.writeObject(propertyOne);
o.writeObject(propertyTwo);
}
private void readObject(ObjectInputStream o)
throws IOException, ClassNotFoundException {
propertyOne = (String) o.readObject();
propertyTwo = (String) o.readObject();
validate();
}
private void validate(){
if(propertyOne == null ||
propertyOne.length() == 0 ||
propertyTwo == null ||
propertyTwo.length() == 0){
throw new IllegalArgumentException();
}
}
public String getPropertyOne() {
return propertyOne;
}
public String getPropertyTwo() {
return propertyTwo;
}
}
public class BadApple extends Apple {
private static final long serialVersionUID = 1;
public BadApple(String propertyOne, String propertyTwo) {
super(propertyOne, propertyTwo);
}
}
我的例外是
Exception in thread "main" java.io.InvalidClassException: customserialisation.BadApple; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1620)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1515)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1704)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1342)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at customserialisation.AppleHolder.readObject(AppleHolder.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at customserialisation.AppleHolder.deSerialize(AppleHolder.java:79)
at customserialisation.AppleHolder.main(AppleHolder.java:61)
我认为这将允许我通过捕获异常而不是部分apples
数组来挽救“otherData”
我试过的2
根据Alexander Torstling的描述,我试图从反序列化的片段中重新创建数组
apples=new Apple[appleCount]
for(int i=0; i<appleCount; i++) {
try {
apples[i]= o.readObject());
} catch(Exception e) {
//Add null or nothing or what you want.
apples[i]=null;
}
}
我希望从反序列化中走出来(因为badApple已经改变了,无法反序列化)
[GoodApple]
[GoodApple]
[GoodApple]
[null]
[null]
我希望在无法实现向后兼容性或删除之前安装的对我的程序的第三方修改的情况下提供一个后备方案我不确定我是否完全理解您的问题,但由于您不想中断阵列反序列化,我认为您无法捕获并继续任何有意义的操作我看到的其他选项是编写自定义数组反序列化例程或为BadApple编写自定义反序列化程序。如果向后兼容很困难,那么只将字段设置为伪值并设置指示“坏条目”的标志怎么样?问题是不能修改BadApple类吗 编辑:顺便说一句,如果您不想了解数组反序列化是如何完成的,请查看ObjectInputStream#readArray。看起来可以复制和修改该代码以支持您的场景,但如果我是您,我会坚持支持旧版本的反序列化,看起来不那么麻烦 EDIT2:我还没能想出一种真正直接的标准技术,在不编辑BadApple的情况下消除bad。我想你可能必须使用自定义反序列化来启动你自己的集合序列化,在反序列化时跳过bad Apple。如果你直接在AppleH中执行,它会是什么样子的较旧(我会使用此功能创建一个单独的列表类型,但我认为此示例更清晰):
公共类AppleHolder实现可序列化{
苹果魔术的静态整数开始=120;
List apples=new ArrayList();
双倍其他数据=15;
私有void writeObject(ObjectOutputStream o)
抛出IOException{
o、 writeInt(apples.size());
(苹果a:苹果){
o、 写(苹果魔术的开始);
o、 书面对象(a);
}
o、 writeObject(其他数据);
}
私有void readObject(ObjectInputStream)
抛出IOException,ClassNotFoundException{
int appleCount=o.readInt();
apples=新阵列列表(appleCount);
对于(int i=0;i如果您无法更改BadApple
,并且需要在AppleHolder
中解决它,我认为您运气不好。但是如果可以,请查看,尤其是:
类,该类在替换实例时需要指定替换
从流中读取应该使用
准确的签名
ANY-ACCESS-MODIFIER对象readResolve()抛出
ObjectStreamException;
因此,将此添加到BadApple
中应该可以做到:
private Object readResolve() throws ObjectStreamException { return null; }
编辑:您可能需要让它显式地实现Serializable
,这样才能工作,所以这可能不是您想要的。我已经澄清了我的问题-uncertainty@RichardTingle我现在明白了,请看我的更新。这似乎是因为下一个好的o.readObject();
。在这种情况下otherData=(双精度)o.readObject()
最终出现异常java.lang.String不能转换为java.lang.Double
。我假设每个苹果包含两个字符串,它们没有被badApple反序列化过程使用,因为badApple从来没有反序列化过是的,我想你是对的。我四处查看,找不到任何跳过对象的方法流中的t。所有的“readFields”和相关方法只能根据API从类本身调用。问题现在转移到检测对象的结尾。也许如果您可以在每个对象写入之前加上大小前缀,这样您就知道要跳过多少字节了?我会想出一个好方法。@RichardTingle好的,这样我就可以想出t两个不错的策略。一个是在每个“苹果”前面加上前缀使用半唯一的魔法字节序列。当反序列化失败时,逐字节读取,直到达到魔法字节。这应该意味着您在下一个苹果上。但这不能保证,因此您可能会得到误报并处理这些错误。更正确的版本是在每个苹果前加上大小前缀,并执行mark()在读取苹果之前,重置()并在反序列化失败后跳过bad apple。这需要访问底层流,这可以通过各种黑客方式完成。这解决了我50%的问题,并且很好地继续进行。有时我控制bad apple,如果这会影响序列化过程而不是反序列化过程,我会在这些情况下使用它ess(即序列化文件中的替代文件)我可以将其作为编码标准的一部分并100%求解。我将有一个剧本我认为在反序列化后调用readResolve,因此我认为您需要使readObject不首先抛出。查看此readResolve()似乎总是被调用,而不是只在坏事情发生时才被调用,所以即使坏事情没有发生,我的坏苹果也会以空结束。这是一个与此相关的问题,在这个问题中,相同的问题通过
public class AppleHolder implements Serializable{
static int START_OF_APPLE_MAGIC=120;
List<Apple> apples=new ArrayList<Apple>();
double otherData=15;
private void writeObject(ObjectOutputStream o)
throws IOException {
o.writeInt(apples.size());
for(Apple a: apples) {
o.write(START_OF_APPLE_MAGIC);
o.writeObject(a);
}
o.writeObject(otherData);
}
private void readObject(ObjectInputStream o)
throws IOException, ClassNotFoundException {
int appleCount = o.readInt();
apples = new ArrayList<Apple>(appleCount);
for(int i=0; i<appleCount; i++) {
try {
while(o.read() != START_OF_APPLE_MAGIC) {
//fast forward to boundary. Maybe add a max here to avoid infinite loops.
}
apples.add((Apple) o.readObject());
} catch(SomethingWentBadException e) {
//Add null or nothing or what you want. Look out for failures caused by
//the value of START_OF_APPLE_MAGIC contained in ordinary fields
apples.add(null);
}
}
otherData = (double) o.readObject();
}
}
private Object readResolve() throws ObjectStreamException { return null; }