加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 当对象具有不同的serialVersionUID时,如何反序列化一个

发布时间:2020-12-14 05:28:10 所属栏目:Java 来源:网络整理
导读:我的客户端有一个oracle数据库,一个对象通过objOutStream.writeObject作为一个blob字段被持久化,该对象现在有一个不同的serialVersionUID(即使该对象没有变化,也许是不同的jvm版本),当他们尝试去序列化抛出异常: java.io.InvalidClassException: Commission
我的客户端有一个oracle数据库,一个对象通过objOutStream.writeObject作为一个blob字段被持久化,该对象现在有一个不同的serialVersionUID(即使该对象没有变化,也许是不同的jvm版本),当他们尝试去序列化抛出异常:
java.io.InvalidClassException: CommissionResult; local class incompatible: 
 stream classdesc serialVersionUID = 8452040881660460728,local class serialVersionUID = -5239021592691549158

他们没有为serialVersionUID分配一个固定的值,因为从现在开始,有些事情改变了这个异常被抛出.现在他们不想松开任何数据,要做到这一点,我认为最好的是读取对象,对它们进行序列化,并通过XMLEncoder再次保持它们,以避免像当前的“类不兼容”错误那样的未来错误.

显然,对于该对象,serialVersionUID持续存在2个不同的值,因此我想要读取数据,尝试使用一个值,如果失败,则尝试使用其他值.为此,我尝试更改该类的serialVersionUID运用
the ASM api.我已经能够更改值,但是问题是如何使类激活更改,所以当它被反序列化时,objInpStr.readObject()将我的特定serializedVersionUID的修改版本.我做了一个测试类来模拟真正的环境,我拿一个对象(其具有与对象具有不同的serialVersionUID问题的对象)对象名称是Reservation的属性是
CommissionResult:

public class Reservation implements java.io.Serializable {


    private CommissionResult commissionResult = null;

}


public class CommissionResult implements java.io.Serializable{



}


import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.SerialVersionUIDAdder;

public class SerialVersionUIDRedefiner extends ClassLoader {


    public void workWithFiles() {
        try {
            Reservation res = new Reservation();
            FileOutputStream f = new FileOutputStream("/home/xabstract/tempo/res.ser");
        ObjectOutputStream out = new ObjectOutputStream(f);

            out.writeObject(res);

            out.flush();
            out.close();

            ClassWriter cw = new ClassWriter(0); 
             ClassVisitor sv = new SerialVersionUIDAdder(cw); //assigns a real serialVersionUID 
             ClassVisitor ca = new MyOwnClassAdapter(sv); //asigns my specific serialVerionUID value
             ClassReader cr=new  ClassReader("Reservation"); 
              cr.accept(ca,0); 

             SerialVersionUIDRedefiner   loader= new SerialVersionUIDRedefiner(); 
             byte[] code = cw.toByteArray();
             Class exampleClass =        loader.defineClass("Reservation",code,code.length); //at this point the class Reservation has an especific serialVersionUID value that I put with MyOwnClassAdapter

             loader.resolveClass(exampleClass);
             loader.loadClass("Reservation");
             DeserializerThread dt=new DeserializerThread();
             dt.setContextClassLoader(loader);
             dt.run();
    } catch (Exception e) {
            e.printStackTrace();
    }}



import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class DeserializerThread extends Thread {

    public void run() {
        try {
            FileInputStream f2;

            f2 = new FileInputStream("/home/xabstract/tempo/res.ser");

             ObjectInputStream in = new ObjectInputStream(f2);


            Reservation c1 = (Reservation)in.readObject();



            System.out.println(c1);

        } catch (Exception e) {

            e.printStackTrace();
        }
        stop();
    }
}

MyOwnClassAdapter Relevant code:



public void visitEnd() {
        // asign SVUID and add it to the class

            try {

                cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,"serialVersionUID","J",null,new Long(-11001));//computeSVUID()));
            } catch (Throwable e) {
                e.printStackTrace();
                throw new RuntimeException("Error while computing SVUID for x",e);
            }


        super.visitEnd();
    }

测试应该失败,并且java.io.InvalidClassException“本地类不兼容”
因为我在保存文件之后更改了serialVersionUID,并使用了一个新的文件
de文件,但它不会失败,所以这意味着ObjectInputStream.readObject不是
使用我的修改版本的Reservation类.

有任何想法吗?提前致谢.

!!!!!!!!!!!!!更新:

好的,可以重新定义resultClassDescriptor来覆盖流serialVersionUID,但是有些奇怪的事情发生,正如我之前所说的那样
是类的2个版本持久化,对象与serialVersionUID = -5239021592691549158L和其他值为8452040881660460728L这最后一个值是
如果我没有为本地类指定值,则生成该值.

– 如果我没有为serialVersionUID指定一个值,则使用默认值(8452040881660460728L),但是不可能对对象进行解除删除
有另一个值,抛出一个错误,说属性是另一个类型.

– 如果我指定值-5239021592691549158L,则类持久化该值
已成功解除序列化,但不是其他类型,同样的错误类型.

这是错误跟踪:

潜在的致命反序列化操作. java.io.InvalidClassException:覆盖序列化类版本不匹配:local serialVersionUID = -5239021592691549158 stream serialVersionUID = 8452040881660460728
java.lang.ClassCastException:不能将java.util.HashMap的实例分配给com.posadas.ic.rules.common.commisionRules.CommissionResult.statusCode,类型为java.lang.String,例如com.posadas.ic.rules. common.commisionRules.CommissionResult

抛出此错误时,类的值为-5239021592691549158,如果更改
该值是8452040881660460728该类成功解除序列化,那么会发生什么?为什么这个错误试图为错误的类投射?

谢谢

解决方法

豪尔赫我在 http://forums.sun.com/thread.jspa?threadID=518416发现了一个解决方案.

在你的项目中创建下面的类.在创建ObjectInputStream对象的时候,使用DecompressibleInputStream代替它,它使用新版本的Id类反序列化旧对象.

public class DecompressibleInputStream extends ObjectInputStream {

    public DecompressibleInputStream(InputStream in) throws IOException {
        super(in);
    }


    protected ObjectStreamClass readClassDescriptor() throws IOException,ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
        Class localClass = Class.forName(resultClassDescriptor.getName()); // the class in the local JVM that this descriptor represents.
        if (localClass == null) {
            System.out.println("No local class for " + resultClassDescriptor.getName());
            return resultClassDescriptor;
        }
        ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
        if (localClassDescriptor != null) { // only if class implements serializable
            final long localSUID = localClassDescriptor.getSerialVersionUID();
            final long streamSUID = resultClassDescriptor.getSerialVersionUID();
            if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
                final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: ");
                s.append("local serialVersionUID = ").append(localSUID);
                s.append(" stream serialVersionUID = ").append(streamSUID);
                Exception e = new InvalidClassException(s.toString());
                System.out.println("Potentially Fatal Deserialization Operation. " + e);
                resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
            }
        }
        return resultClassDescriptor;
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读