У моего клиента есть база данных оракула, и объект был сохранен как поле большого двоичного объекта через objOutStream.writeObject, теперь объект имеет другое значение serialVersionUID
(даже если у объекта нет изменений, возможно, другая версия jvm) и когда они попытаться десериализовать исключение:
java.io.InvalidClassException: CommissionResult; local class incompatible:
stream classdesc serialVersionUID = 8452040881660460728,
local class serialVersionUID = -5239021592691549158
Они не присваивали фиксированное значение для serialVersionUID
с самого начала, поэтому теперь, когда что-то изменилось, выдается исключение. Теперь они не хотят терять какие-либо данные, для этого, я думаю, лучше всего прочитать объекты, десериализовать их и снова сохранить их с помощью XMLEncoder, чтобы избежать будущих ошибок, таких как текущая ошибка «несовместимость классов».
По-видимому, для этого объекта сохраняются 2 различных значения для serialVersionUID
, поэтому я хочу прочитать данные, попробуйте с одним значением, а в случае сбоя попробуйте другое значение. Для этого я попытался изменить serialVersionUID
класса, использующего
API ASM . Я смог изменить значение, но проблема в том, как активировать изменение в классе, чтобы при десериализации 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, 0, 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
после сохранения файла и использовал новый для чтения
де файл, но это не дает сбоя, так что это означает, что ObjectInputStream.readObject
не
используя мою модифицированную версию класса Reservation
.
Есть идеи? Заранее спасибо.
!!!!!!!!!!!!! UPDATE:
Хорошо, можно переопределить resultClassDescriptor, чтобы переопределить поток serialVersionUID, но, как я уже сказал, до того, как он там появился, происходит нечто странное
две версии класса сохраняются, объекты с 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 класса успешно десериализовано, так что же происходит? почему эта ошибка пытается привести к неправильному классу?
Спасибо