Serialization Proxy

Serialization is often complex for real-world classes. Here comes a nice trick which may be useful in your tool box.

Problem Description

I just had the problem that I had a class which is similar to an enum because it defines constant values. But it also needed to be user-extensible, so I couldn’t use enum as I usually would. So what I had was some kind of extensible singleton which I’ll just christian pseudo-constant for now.

Internally it is storing data which is inherently unserializable, so just extending the Serializable interface would be useless. So how should I do the serialization?

Solution

Luckily each of the values was already required to define a unique signature (a String in this case) and to register itself so it could be looked up based on this signature. So basically on serialization I could just write out this signature, then restore the registered value from the deserialized signature.

At first glance the supported methods for serializing objects are not perfectly helpful:

private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; private void readObjectNoData() throws ObjectStreamException; /*ANY-ACCESS-MODIFIER*/ Object writeReplace() throws ObjectStreamException; /*ANY-ACCESS-MODIFIER*/ Object readResolve() throws ObjectStreamException

First Try: Ugly

At second glance I can replace an object with readResolve(), but that does not have access to the stream. So I’d have to implement a combination of readObject() (which has access to the stream) and readResolve() to replace what I had read with what I wanted.

readObject() already runs on a new object for which I could read the signature, store it somewhere (there was no field for it yet, but that could basically be added), then I could return the real object from readResolve() by looking up the signature. For that I’d have to make all other fields transient and only leave the signature field for serialization. This seemed possible, but had a certain ugliness to it, mostly for introducing an otherwise completely unnecessary field.

Second Try: Awful

My second idea was even worse, because I thought that I could put the burden on the classes which were using my pseudo-constants. The idea was to add two static methods

void writePseudoConstant(java.io.ObjectOutputStream out, PseudoConstant constant) { // write out the signature } PseudoConstant readPseudoConstant(java.io.ObjectInputStream in) { // read the signature, then return the registered constant }

These could be used by classes with PseudoConstant fields to serialize and deserialize these fields. But implementing self-defined writeObject() and readObject() methods for non-trivial classes really is a pita, and mostly fragile or even plain impossible.

Last Try: Nice and Clear

In the end I followed the old programmer’s rule if you are stuck insert an indirection and implemented a dedicated proxy class which is serialized and deserialized instead of my pseudo-constant class. For this my class implements Serializable and just the writeReplace() method:

public class PseudoConstant implements java.io.Serializable { // ... lots of non-serializable fields ... public String signature() { // return signature } private Object writeReplace() throws java.io.ObjectStreamException { return new PseudoConstantSerializationProxy(this); } // [...] public static PseudoConstant getRegistered(String signature) { // return the registered constant for the given signature } }

and the proxy class looks like

final class PseudoConstantSerializationProxy implements java.io.Serializable { private static final long serialVersionUID = -396647803428144681L; private final String signature; /** * Constructor. * @param constant pseudo constant for which this proxy is used */ PseudoConstantSerializationProxy(PseudoConstant constant) { this.signature = constant.signature(); } /** * Resolve this proxy back to the original pseudo constant. * @return pseudo constant */ Object readResolve() throws ObjectStreamException { return PseudoConstant.getRegistered(signature); } }

In most contexts this should be a static inner class of PseudoConstant, to not pollute the public name space. But in my case it was preferable this way because I had different types of pseudo-constants which could all use the same proxy.