我试图以这样的方式编写对象反序列化:如果一个对象数组包含某些对象(由于代码更改)无法反序列化,那么数组中的那些引用将变为空而不是抛出异常;允许对象的其余部分进行抢救。
我试图使用自定义序列化/反序列化,希望我能捕获异常并应用我的自定义“make it null”逻辑。其代码如下。然而,我似乎能够捕获异常的第一点是整个数组反序列化已经失败。
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”。
根据Alexander Torstling的answer我试图从反序列化的片段中重新创建数组
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中可能包含的任何o.readObject()(在这种情况下是两个字符串),因此反序列化的数据不同步,在我的情况下,我得到一个转换异常,因为otherData = (double) o.readObject();
读取应该属于BadApple的String
,因为它从未从流中消耗过。
如何打捞序列化数组,其中只有部分对象可反序列化?从而获得具有非可反序列化部分的空条目的数组。在我的数组中,我在一个数组中有几个对同一个对象的引用,在反序列化过程中保留它是至关重要的。
所以进入序列化我有
[GoodApple]
[GoodApple]
[GoodApple]
[BadApple]
[BadApple]
我想要反序列化(因为badApple已经改变,无法反序列化
[GoodApple]
[GoodApple]
[GoodApple]
[null]
[null]
我希望这能提供一个后备,在这种情况下无法实现向后兼容,或者删除了之前安装的程序的第三方修改
答案 0 :(得分:1)
我不确定我是否完全理解你的问题,但由于你不想打断数组反序列化,我认为你不能以任何有意义的方式捕获并继续。我看到的其他选项是编写自定义数组反序列化例程或为BadApple编写自定义反序列化器。如果向后兼容很难,那么只是将字段设置为伪值并设置一个表示“错误输入”的标志呢?问题是你无法修改BadApple类吗?
编辑:顺便说一句,如果你不想看看数组反序列化的完成方式,请查看ObjectInputStream #readArray。看起来可以复制和修改代码来支持你的场景,但如果我是你,我会坚持支持旧版本的反序列化,似乎不那么混乱。
EDIT2:我没有能够提出任何真正直接的标准技术来在不编辑BadApple的情况下清除错误。我认为您可能必须使用自定义反序列化来滚动自己的集合序列化,这会在反序列化时跳过坏苹果。如果你直接在AppleHolder中做它会是什么样子的草图(我会用这个功能制作一个单独的列表类型,但我认为这个例子更清晰):
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();
}
}
我使用了一个列表,因为如果你不回读所有对象并且不使用空占位符(未知的确切回读大小)会更容易。该示例应该与数组一起使用,但只需要进行少量调整。
EDIT3:我用魔术边界值更新了示例。这真是个hackish。我使用了一个字节值,否则我们无法确定对象反序列化是否读取了偶数字节(对齐问题)。
答案 1 :(得分:0)
如果您无法更改BadApple
并需要在AppleHolder
中解决,我认为您运气不好。但如果可以,请查看javadoc for Serializable
,特别是:
需要在其实例时指定替换的类 从流中读取应该实现这个特殊的方法 确切的签名。
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
因此,将此添加到BadApple
应该可以解决问题:
private Object readResolve() throws ObjectStreamException { return null; }
编辑:您可能需要明确实施Serializable
才能使其正常工作,因此这可能不是您想要的。