So, a while ago I wrote a SerializableImage class in order to easily bundle several images together into a single file. The class worked fine, both serializing, file I/O and drawing.

In another project I'm working on I successfully used a copy of this SerializableImage class, until recently. I had decided that I wanted my project in a "real" package, rather than the default_package (due to some jar-ing problems). Now, all of a sudden, my SerializableImage class starts generating ClassNotFound exceptions when I try to cast the objects from my image file to SerializableImage.

Nothing in the class has changed except that I added

package myPackage;

to the header of my classes. Class structure and serialVersionUID are exactly the same. The class is found, since I'm able to instanciate SerializableImage objects.

Does anyone know a way to get around this problem, or will I have to admit defeat, change serialVersionUID and re-generate all my image bundles again with the new class version?

Thanks for your time!
Emil Olofsson

Dani AI

Generated

Java's serialization embeds the class' fully qualified name plus its serialVersionUID in the stream. Moving a class into a package changes that name, so an ObjectInputStream trying to load the old descriptor will fail with ClassNotFoundException even if a logically identical class exists under a new package. That is why old serialized blobs become unreadable just by renaming or repackaging the class.

A practical, non-destructive migration is to provide a mapping from the old name to the current Class when deserializing. The following custom ObjectInputStream overrides name resolution and lets an old stream instantiate the new class directly.

import java.io.*;
import java.util.*;

public class RenamingObjectInputStream extends ObjectInputStream {
    private final Map<String,Class<?>> map = new HashMap<>();

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

    public void addMapping(String oldName, Class<?> newClass) {
        map.put(oldName, newClass);
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException {
        Class<?> c = map.get(desc.getName());
        if (c != null) return c;
        return super.resolveClass(desc);
    }
}

Using this, a conversion tool can open each old file with the RenamingObjectInputStream, register a mapping such as "SerializableImage" -> my.package.SerializableImage.class (or "old.pkg.Name" if it had a package), read the object, and then reserialize in the current format. An alternative is a temporary “shim” class with the original fully qualified name that implements Serializable and either exposes the old fields for conversion or implements readResolve to return a new-class instance; note that classes in the default package cannot be imported by named packages, so a short-lived shim may need to live on the classpath only for the migration.

Keep serialVersionUID stable across versions when compatibility is intended; mismatched values cause InvalidClassException even if class names match. For long-term robustness consider a version-tolerant format (JSON, protobuf, or a serialization-proxy) or keep a small migration utility in the project. This follows the practical points raised by and the conversion idea from , and explains why 's repack succeeded after regenerating the image bundles.

Recommended Answers

All 3 Replies

Are you saying that you saved the images to a file, then changed the class and then tried to read? If yes then Yes you will not be able to read them because:
You serialized instances of this: SerializableImage. the files containes this: SerializableImage.
But whan you read them you are trying to pass that to a: myPackage.SerializableImage instance.
This will compile:

myPackage.SerializableImage image = (myPackage.SerializableImage)readObject();

But if the read object doesn't return an myPackage.SerializableImage object the program will crash. And the files contain SerializableImage objects.

So you will have to serialize the images again. Or for a more "professional" solution: Write a prgram that reads all the files and saves them into a SerializableImage. Then use the values of those objects to create mypackage.SerializableImage objects and save those by replacing the old files. you will have to do the above only once and the files will work from now on.

the class changed, therefore any old serialised instances are now incompatible with the new class structure.

Yeah I figured. I had already tried adding myPackage as prefix, but with no luck. Luckily my image packaging program also generated txt-files containing the filenames of all images in a package in case something like this would happen, so repacking was easy.

Thanks for clearing this out for me!
Emil Olofsson

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.