How to Deserialize the Object, If It Was Moved to Another Package or Renamed

How can I deserialize the object, if it was moved to another package or renamed?

Question: Is it possible to load the
new class instances from this file
using any tricks (except trivial
copying the class into old package and
then using the deserialization wrapper
logic)?

I don't think there are any other "tricks" you could use that don't involve at least a partial reimplementation of the serialization protocol.

Edit: there is in fact a hook that allows this if you control the deserialization process, see the other answer.

It is possible to use readResolve() to
recover from moving/renaming the
class? If not, please, explain why.

No, because the deserialization mechanism will fail much earlier, at the stage where it tries to locate the class that's being deserialized - it has no way of knowing that a class in a different package has a readResolve() method it's supposed to use.

ClassNotFoundException when deserializing after refactored package

ClassNotFoundException: com.company.Clerk

This means it could not find the class com.company.Clerk so you would need to have in your JAR com/company/Clerk.class or have the directory above com/company/Clerk.class in your class path.

In short, the package has to match the directory the class is in.

he stated he REFACTORED the code and changed the package and wants to deserialize something that was serialized before the refactoring

This won't change the serialization format and the standard serialization doesn't support aliases or renaming. What you need to deserialize the original class and convert it to your new class at runtime.

C# Deserialize a class which has moved or been renamed

If you have moved it, then add a reference to the dll where it now resides, and use TypeForwardedToAttribute:

[assembly:TypeForwardedTo(typeof(TheType))]

This will be enough for some requests (including BinaryFormatter IIRC) looking for a type to find it in the new assembly. However, IIRC it only works for outermost types (not nested types, and probably not generics), and you can't have renamed it / changed the namespace / etc.

Renaming is tricker... BinaryFormatter is notoriously brittle about such things. IMO, it is only suitable for serializing transient data between two tightly coupled systems (for example, exchange between two AppDomains in the same process; when used for storage, or between systems that might get out of sync, it can be a nightmare.

It may be too late, but I would recommend using a contract-based serializer (rather than a type-based serializer); any of XmlSerializer, DataContractSerializer (as long as you use the [DataContract]/[DataMember] attributes), etc. Of if you want fast binary, protobuf-net would do a good job (and can hook into ISerializable if you need).

Another concept that might be worth looking at is serialization surrogates, but that is relatively hard. But IIRC this gives you the control over the type that is created - but you need to do a lot of the work for it.

How to deserialize object to new object with the same name?

This was answered in the comments but the one with the answer didn't post it as such.

I ended up moving the old class to a different package and converted the class when the user ran the program again.

All I had to do then was wait for my users to all update the the new version and deleted the old class.

Thank you!

How to deserialize a binary formatted object whose type got modified as an abstract class?

My preferred way to deal with things like this is to have a serialization-layer. All objects get converted to a corresponding serialization object, and back again.

If there is a significant change in the object hierarchy you would rename the serialization object to something like MyObjectLegacy or MyObject_v0 . The conversion code can then convert the old serialization object to whatever new object(s) you want to use. This helps keep serialization separated from the rest of the object model.

If you lack a serialization layer I see few other options than keeping the old class around just so you can unpack it. You might be able to use binding redirects or other stuff to be able to rename the class, but this is a bit fragile in my experience.

I would encourage you to switch to something other than BinaryFormatter. It is slow, fragile, and produces relatively large files. There are much better alternatives, like protobuf.Net, Bson, json etc. Take a look at a comparison. We had some problems when BinaryFormatter changed format between different .Net versions, not fun.

If i changed the package of a java class. Will the deserialization from the old serialized version still work?

The package to which a class belongs is a fundamental part of that class's identity, as reflected by the package name being part of the class's fully-qualified name. For all practical intents and purposes, changing the package to which a class is assigned drops the original class and replaces it with a completely different class.

In particular, if you serialize an instance of a class named "my.package.MyClass" via Java serialization, then successfully deserializing the result always yields an instance of a class named "my.package.MyClass". If no such class can be loaded, or if the serialization version of the one that is loaded does not match that of the one that was serialized, then deserialization will fail.

If you retain the old class along with the new class then you can perhaps patch up the deserialization problem in the new version of the application. Simply be prepared for objects of the old class, and convert them to instances of the new class as soon as you deserialize them. But if you want the new version of the application to play well with the old, then you must also do the reverse when the new one serializes objects of the affected class, else you will just cause the old application to have deserialization errors. At this point, you should be considering whether changing the package name was all that important after all.

Overall, Java serialization is not well suited for object storage. It is primarily targeted at object communication. You might consider switching to an XML-, JSON-, or YAML-based serialization format, which at least you could twiddle between cache and consumer. Such a change would of course be incompatible with the old version of your application, but so, apparently, is the package change you have already performed.

Using readClassDescriptor() and maybe resolveClass() to permit Serialization versioning

I had same problems with flexibility like you and I found the way.
So here my version of readClassDescriptor()

    static class HackedObjectInputStream extends ObjectInputStream
{

/**
* Migration table. Holds old to new classes representation.
*/
private static final Map<String, Class<?>> MIGRATION_MAP = new HashMap<String, Class<?>>();

static
{
MIGRATION_MAP.put("DBOBHandler", com.foo.valueobjects.BoardHandler.class);
MIGRATION_MAP.put("DBEndHandler", com.foo.valueobjects.EndHandler.class);
MIGRATION_MAP.put("DBStartHandler", com.foo.valueobjects.StartHandler.class);
}

/**
* Constructor.
* @param stream input stream
* @throws IOException if io error
*/
public HackedObjectInputStream(final InputStream stream) throws IOException
{
super(stream);
}

@Override
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
{
ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

for (final String oldName : MIGRATION_MAP.keySet())
{
if (resultClassDescriptor.getName().equals(oldName))
{
String replacement = MIGRATION_MAP.get(oldName).getName();

try
{
Field f = resultClassDescriptor.getClass().getDeclaredField("name");
f.setAccessible(true);
f.set(resultClassDescriptor, replacement);
}
catch (Exception e)
{
LOGGER.severe("Error while replacing class name." + e.getMessage());
}

}
}

return resultClassDescriptor;
}

Java serialization: readObject() vs. readResolve()

readResolve is used for replacing the object read from the stream. The only use I've ever seen for this is enforcing singletons; when an object is read, replace it with the singleton instance. This ensures that nobody can create another instance by serializing and deserializing the singleton.



Related Topics



Leave a reply



Submit