Problem mit Serializable --> serialVersionUID

jorgeHX

Erfahrenes Mitglied
Hallo zusammen,
ich hab ein Problem mit dem Serializable.

Ich habe eine Import und Exportfunktion geschrieben, die auch soweit läuft. Allerdings tritt beim Importieren ein Fehler auf, wenn ich eine Datei von einer älteren Version in die neueste Version importieren möchte.

Code:
ava.io.InvalidClassException: import_export.objekte.Baum; local class incompatible: stream classdesc serialVersionUID = 8191912955886397793, local class serialVersionUID = 4087753089674047772

Wie kann ich diesen Fehler umgehen? Ich hab es schon mit

Code:
static final long serialVersionUID = 4087753089674047772L;

versucht. Leider ohne Erfolg. Darüberhinaus will ich es lieber nicht statisch zuweisen, sondern eher jede VersionUID zulassen, solange das zu importierende Format stimmt.
Kann mir da jemand weiterhelfen? Hat jemand ein Beispiel?

Vielen Dank,
JP
 
Hallo,

das Problem hier ist, dass du versuchst alte Klassendefinition (altere SerialVersionId) mit einer neuen zu matchen... man sollte die SerialVersionId nur dann ändern, wenn man neue Attribute zu einer Klasse hinzugefügt bzw. geändetr hat. Sonst bekommt man schnell inkompatibilitäten 8wie in deinem Beispiel zu sehen). Das ist ein Problem der klassischen Objekt-Serialisierung in Java. Ich würde dir Raten die Daten in XML zu Exportieren/Importieren. Dann hast du keine solchen Probleme... (bzw. andere Probleme) ;-) Um die alten Daten zu migrieren würde ich mir (in einem separatem Projekt) die alten Objekte wieder deserialiseren (sprich Domain Klassen mit Kompatiblen SerialVersionId ausstatten) und einlesen. Dann entsprechend in einem anderen Format (XML) serialiseren.
Das gleiche macht man mit den aktuellen Daten. Anschließend benutzt man nur noch die XML basierte Serialisierung für Import / Export.

Gruß Tom
 
Moin Thomas,
gibt es nicht noch ne andere Variante wie ich das regeln kann? Ich habe nämlich schon viele Dateien von der alten Version exportiert und will die nun wieder importieren. Allerdings kommt die bekannte Fehlermeldung.

Kann man nicht irgendwie "readObject()" überschreiben, so dass die VersionsUID nicht mehr geprüft wird beim importieren?

Ich hab allerdings keine Ahnung wie das gehen könnte :-(

Grüße,
JP
 
Kann mir jemand verraten, warum es nicht funktioniert, wenn ich

Code:
static final long serialVersionUID = 4087753089674047772L;

vorab definiere? Gibt es generell die Möglichkeit die serialVersionUID während der laufzeit zu verändern?

Schon jetzt vielen Dank,
JP
 
Hallo,

ändern könnte man die schon zur Laufzeit über Reflection, aber dann gäbs unter umständen Probleme wenn du zwischendurch Elemente mit einer anderen SerialVersionId laden willst... da müsstest du jedes mal umswitchen, oder für jede Klassen Version einen eigenen ClassLoader verwenden, so dass du unterschiedliche Versionen / Repräsentationen der Klasse gleichzeitig Laden kannst... das macht die Arbeit damit aber sehr kompliziert.... wie gesagt ich würde die alten Objekte einfach wieder einlesen (einfach SerialiVersionId auf den alten wert setzen und diese wieder in einem evolutionsfreundlicherem Format (XML) abspeichern).

Gruß Tom
 
Ähm, wie meinst du das mit dem einlesen. Wenn ich das Object einlese, kommt direkt die oben gezeigte Fehlermeldung.
Ich würde ja gerne die alte Datei einlesen und dann einfach die alte ID ignorieren. Geht das nicht einfach?

Sorry, wenn die Frage dumm klingt :-)

Grüße
 
Hallo,

also wenn ich Tom richtig verstanden habe, sollst du einfach in deiner Klasse die SerialVersionUID wieder zurück auf den alten Wert setzen, die alten Objecte dann auslesen und mit der von ihm genannten API wieder abspeichern und so hättest du das Problem aus der Welt geschafft.

MFG

zEriX
 
Hallo,

alte version:
Java:
/**
 * 
 */
package de.tutorials;

import java.io.Serializable;

/**
 * @author Thomas.Darimont
 *
 */
public class SerializableObject implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 37289463820437L;
    
    int intValue;
    String stringValue;
    
    
    
    
    /**
     * 
     */
    public SerializableObject() {
        super();
        // TODO Auto-generated constructor stub
    }
    /**
     * @param intValue
     * @param stringValue
     */
    public SerializableObject(int intValue, String stringValue) {
        super();
        this.intValue = intValue;
        this.stringValue = stringValue;
    }
    /**
     * @return the intValue
     */
    public int getIntValue() {
        return intValue;
    }
    /**
     * @param intValue the intValue to set
     */
    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }
    /**
     * @return the stringValue
     */
    public String getStringValue() {
        return stringValue;
    }
    /**
     * @param stringValue the stringValue to set
     */
    public void setStringValue(String stringValue) {
        this.stringValue = stringValue;
    }
    
    @Override
    public String toString() {
        return this.intValue +" " + this.stringValue;
    }
}

Serialisieren / Deserialisieren:
Java:
/**
 * 
 */
package de.tutorials;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * @author Thomas.Darimont
 * 
 */
public class SerialVersionIdIgnoringObjectInputStreamExample {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        File objectStore = new File("c:/objectStore.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(
                new FileOutputStream(objectStore));
        objectOutputStream.writeObject(new SerializableObject(4711, "bubu"));
        objectOutputStream.close();

        ObjectInputStream objectInputStream = new ObjectInputStream(
                new FileInputStream(objectStore));
        SerializableObject serializableObject = (SerializableObject) objectInputStream
                .readObject();
        objectInputStream.close();
        System.out.println(serializableObject);

    }

}

Ausgabe:
Code:
4711 bubu

Neue Version:
Java:
/**
 * 
 */
package de.tutorials;

import java.io.Serializable;

/**
 * @author Thomas.Darimont
 *
 */
public class SerializableObject implements Serializable{
    /**
     * new
     */
    private static final long serialVersionUID = -6518072243425999514L;
    
    /**
     * old 
     */
    //private static final long serialVersionUID = 37289463820437L;
    
    
    int intValue;
    String stringValue;
    double doubleValue;
    
    
    
    
    /**
     * 
     */
    public SerializableObject() {
        super();
        // TODO Auto-generated constructor stub
    }
    /**
     * @param intValue
     * @param stringValue
     */
    public SerializableObject(int intValue, String stringValue) {
        super();
        this.intValue = intValue;
        this.stringValue = stringValue;
    }
    
    /**
     * @param intValue
     * @param stringValue
     * @param doubleValue
     */
    public SerializableObject(int intValue, String stringValue,
            double doubleValue) {
        super();
        this.intValue = intValue;
        this.stringValue = stringValue;
        this.doubleValue = doubleValue;
    }
    /**
     * @return the intValue
     */
    public int getIntValue() {
        return intValue;
    }
    /**
     * @param intValue the intValue to set
     */
    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }
    /**
     * @return the stringValue
     */
    public String getStringValue() {
        return stringValue;
    }
    /**
     * @param stringValue the stringValue to set
     */
    public void setStringValue(String stringValue) {
        this.stringValue = stringValue;
    }
    /**
     * @return the doubleValue
     */
    public double getDoubleValue() {
        return doubleValue;
    }
    /**
     * @param doubleValue the doubleValue to set
     */
    public void setDoubleValue(double doubleValue) {
        this.doubleValue = doubleValue;
    }

    @Override
    public String toString() {
        return this.intValue +" " + this.stringValue + " " + this.doubleValue;
    }
    
}

Diesmal nur auslesen:
Java:
/**
 * 
 */
package de.tutorials;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * @author Thomas.Darimont
 * 
 */
public class SerialVersionIdIgnoringObjectInputStreamExample {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        File objectStore = new File("c:/objectStore.ser");
//        ObjectOutputStream objectOutputStream = new ObjectOutputStream(
//                new FileOutputStream(objectStore));
//        objectOutputStream.writeObject(new SerializableObject(4711, "bubu"));
//        objectOutputStream.close();

        ObjectInputStream objectInputStream = new ObjectInputStream(
                new FileInputStream(objectStore));
        SerializableObject serializableObject = (SerializableObject) objectInputStream
                .readObject();
        objectInputStream.close();
        System.out.println(serializableObject);

    }

}

Ergibt:
Code:
Exception in thread "main" java.io.InvalidClassException: de.tutorials.SerializableObject; local class incompatible: stream classdesc serialVersionUID = 37289463820437, local class serialVersionUID = -6518072243425999514
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:562)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1583)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
    at de.tutorials.SerialVersionIdIgnoringObjectInputStreamExample.main(SerialVersionIdIgnoringObjectInputStreamExample.java:31)

Setzt man nun die SerialVersionId des SerializableObjects wieder auf die alte Id:
/**
* new
*/
//private static final long serialVersionUID = -6518072243425999514L;

/**
* old
*/
private static final long serialVersionUID = 37289463820437L;

So kann man die alten Objekte wieder deserialisieren.

Code:
4711 bubu 0.0
Jedoch bleiben dann die neuen Attribute (die damals nicht mitserialisiert wurden) mit ihren default Werten initialisiert. Das wär dann mal eine Möglichkeit die Daten für eine Migration wieder einzulesen ...

also wenn ich Tom richtig verstanden habe, sollst du einfach in deiner Klasse die SerialVersionUID wieder zurück auf den alten Wert setzen, die alten Objecte dann auslesen und mit der von ihm genannten API wieder abspeichern und so hättest du das Problem aus der Welt geschafft.
So ganz unproblematisch ist dieser Ansatz nicht... die SerialVersionId ist eine Art hashCode einer serialisierbaren Klasse wenn man ander Klasse was ändert sollte man auch die SerialVersionId neu erstellen. Binärkompatibilität sollte man eher auf einem anderen Level erreichen...

Gruß Tom
 
Hallo,
erstmal danke für das Beispiel.

Gibt es denn in Java auch eine Methode um aus einer File f die SerialVersionUID auszulesen?

Das würde mir nämlich am ehesten weiterhelfen.

Danke vielmals,
JP
 
Zurück