繁体   English   中英

Java反序列化向后兼容性

[英]Java de-serialization backward compatibility

如何反序列化在序列化后修改的类?

更具体地说,我知道当类的初始版本中具有serialVersionUID时,可以完成此操作。 有什么方法可以对没有serialVersionUID类执行此操作?

我有一个对象

package com.test.serialize;

import java.io.Serializable;

public class MyObject implements Serializable{

    String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

我像这样序列化类

package com.test.serialize;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializeTest {

    public static void main(String[] args) {

        try {
            MyObject myObject = new MyObject();
            myObject.setName("Ajit");

            ObjectOutputStream objectOStr = null;
            ByteArrayOutputStream byteOStr = null;
            try {
                byteOStr = new ByteArrayOutputStream();
                objectOStr = new ObjectOutputStream(byteOStr);
                objectOStr.writeObject(myObject);

            } catch (IOException e) {
                System.out.println(e);
            } finally {
                try {
                    if (objectOStr != null)
                        objectOStr.close();
                } catch (IOException e2) {
                }
            }
            FileOutputStream fo = new FileOutputStream(new File("serialize"));
            fo.write(byteOStr.toByteArray());
            fo.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

像这样反序列化

package com.test.serialize;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.RandomAccessFile;

public class DeserializeTest {

    public static void main(String[] args) {

        try {

    //          File f = new File("serialize");
    //          FileInputStream fs = new FileInputStream(f);
            RandomAccessFile raF = new RandomAccessFile("serialize", "r");
            byte[] b = new byte[(int)raF.length()];
            raF.read(b);

            ObjectInputStream oIstream = null;
            ByteArrayInputStream bIstream = null;

            bIstream = new ByteArrayInputStream(b);
            oIstream = new ObjectInputStream(bIstream);
            Object finalResult = oIstream.readObject();
            System.out.println(finalResult.toString());
        } catch (IOException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

一段时间后,我添加了

@Override
public String toString() {
    return "MyObject [name=" + name + ", names=" + names + "]";
}

MyObject 在添加之后,我得到了类似的异常

java.io.InvalidClassException: com.test.serialize.MyObject; local class in 
compatible: stream classdesc serialVersionUID = 5512234731442983181, local class
serialVersionUID = -6186454222601982895
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at com.test.serialize.DeserializeTest.main(DeserializeTest.java:25)     

请帮我解决一下这个。

感谢@GáborBakos。

这可以通过为较旧的类创建serialVersionUID来解决(其签名应与序列化期间的签名相同),然后在当前类中添加该serialVersionUID。

serialver -classpath /***PATH***/bin com.test.serialize.MyObject

那回来了

com.test.serialize.MyObject:    static final long serialVersionUID = 5512234731442983181L;

之后,我将其添加到我的MyObject中,如下所示

package com.test.serialize;

import java.io.Serializable;

public class MyObject implements Serializable{

    /**
     * Added serial version Id of old class, created before adding new fields
     */
    private static final long serialVersionUID = 5512234731442983181L;


public MyObject() {
    System.out.println("Constructor");
}

String name;


public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}


String names ="Altered after change!";

public String getNames() {
    return names;
}

public void setNames(String names) {
    System.out.println("Setting names");
    this.names = names;
}

@Override
public String toString() {
    return "MyObject [name=" + name + ", names=" + names + "]";
}

}

工作正常。

更多信息请参考: serialver

第一个建议:使用序列化,因为一切都差不多了。

第二条建议:使用serialVersionUID并使用一个版本对其进行修复:在这里警告您并防止在不同序列化版本之间造成混淆。

因此:如果更改字段或字段的含义,请更改serialVersionUID。

然后您有向后兼容性问题。

请参阅以下内容以获得许多想法: 管理序列化Java对象的多个版本

恕我直言:

  • 无论采用哪种解决方案,请记住您的程序将使用部分数据来管理对象:然后,您必须使用或不使用数据来管理所有案例。

  • 如果您不经常更改版本,请使用几个不同的类。 也许是子类或接口的实现:然后,您可以获取程序,并管理多个版本的对象:MyClass_V1,MyClass_V2等。反序列化时,可以测试/重试并获得良好的Object。 之后,您可能必须在各个类之间转换数据

  • 如果您更改版本,则通过添加新字段(而不更改旧字段),会稍微容易一些(子类,直接转换为父级)

  • 或者您可以考虑使用XML结构进行序列化和反序列化:您可以具有向后和向前的兼容性,因为它是可扩展的 :字段存在或为空。 您必须自己管理映射或使用一些库。

希望能帮助到你 !

我会记住以下几点,

  1. 每个Serializable类都包含一个serialVersionUID (是否明确指定了一个都没有关系)。
  2. 有兼容的更改,有不兼容的更改
    例如,添加新字段是兼容更改,删除字段不是兼容更改。 添加/删除/编辑方法通常是兼容的更改,但是在您的情况下,肯定不是这样(添加toString()方法后更改了serialVersionUID

3,在修改类之前,可以使用serialver实用工具找到旧类的serialVersionUID并在新类中使用它

不要以为还有其他魔术:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM