Making XMLEncoder do something sensible with binary data

By default, XMLEncoder encodes byte arrays like any other array:

   <array class="byte" length="8">
    <void index="0">
     <byte>115</byte>
    </void>
    <void index="1">
     <byte>111</byte>
    </void>
    ...
   </array>

Repeat for every byte. This is bad.

A reasonable solution would be to store the whole byte array as a base 64 encoded string. Here’s how (it took a bit of figuring out):


public class ByteArrayPersistenceDelegate extends PersistenceDelegate {
    protected Expression instantiate(Object oldInstance, Encoder out) {
        byte[] e = (byte[]) oldInstance;
        return new Expression(e, ByteArrayPersistenceDelegate.class, "decode",
                new Object[]{ByteArrayPersistenceDelegate.encode(e)});
    }

// We want methods from this class to appear in the data - the indirection means we're
    // not tied to a particular Base64 implementation and are protected from interface changes.

public static byte[] decode(String encoded) {
        return org.apache.commons.codec.binary.Base64.decodeBase64(encoded);
    }

public static String encode(byte[] data) {
        return org.apache.commons.codec.binary.Base64.encodeBase64String(data);
    }
}

...

XMLEncoder e = new XMLEncoder(out) {
    public PersistenceDelegate getPersistenceDelegate(Class<?> type) {
        if (type == byte[].class)
            return del;
        else
            return super.getPersistenceDelegate(type);
    }
};
e.writeObject(someByteArrayOrSomeObjectContainingOne);
e.close();

See how you actually have to override getPersistenceDelegate()? You might think you’d setPersistenceDelegate() for byte[], but that doesn’t work – you still end up with the default array delegate and the default output.

That Base64 class is from the Commons Codec library, by the way.

This entry was posted in Uncategorized. Bookmark the permalink.

One Response to Making XMLEncoder do something sensible with binary data

  1. adrian says:

    test