Custom Serialization in Java

Articles —> Custom Serialization in Java

Serialiazation in java is a way to convert an object to and from a stream of bytes, allowing one to not only save an object to file but also transfer an object over a network. Serialization is accomplished by implementing the Serializable interface followed by utilization of the java.io.ObjectInputStream and java.io.ObjectOutputStream classes.

In some contexts, a class may need to customize how it is converted to a byte stream. This can be useful in several different contexts, including:

  • Encryption of the contents of the object for security purposes.
  • Trigger code that must run immediately after de-serialization.
  • Custom compression techniques.

Implementation of the following methods within a class to be serialized allows for the customization of the serialization process:

  • private void writeObject(ObjectOutputStream out) throws IOException
  • private void readObject(ObjectInputStream in) throws IOException

As an example of the use of these methods, take an example in which encryption of an object is necessary for the desired behavior:


/**

* Class to demonstrate the need to customize serialization. 

*/

public class Passfile implements Serializable{



    static final long serialVersionUID = 4321412L;



    private String username;

    private String password;

    private transient String tempData = "";

 

    public Passfile(String user, String pass){

        this.username = user;

        this.password = pass;

    }

   

    public String getUsername(){

        return username;

    }



    public String getPassword(){

        return password;

    }



    public String getLocalData(){

        return tempData;

    }



    public void setTempData(String data){

        tempData = data;

    }

}

The above class demonstrates a potential need for encryption. This class encapsulates a username and password variable and may, for instance, be needed to transfer over a network or save to a file, in which case security precautions are necessary. One can use the readObject/writeObject methods to customize the default serialization protocol, allowing one to encrypt and decrypt data prior to and post serialization (in the code below, the encrypt and decrypt methods encrypt and decrypt the Strings and can use any number of encryption techniques, thus these methods were left out for brevity):


private void writeObject(ObjectOutputStream oos) throws IOException{

    String encryptedUsername = encrypt(username);

    String encryptedPassword = encrypt(password);

    oos.writeObject(encryptedUsername);

    oos.writeObject(encryptedPassword);

}

private void readObject(ObjectInputStream in) throws IOException{

    username = decrypt((String)in.readObject());

    password = decrypt((String)in.readObject());

}

Another use for custom serialization is in the instantiation of transient values. Transient values may default to null upon default de-serialization, thus potentially causing NullPointerExceptions. Examples can include listeners or data which can be readily calculated. Consider the following:




/**

* Class to demonstrate default values for transient objects. 

*

*/

public class Example implements Serializable{



    static final long serialVersionUID = 4321421L;

    private String name = "";

 

    private transient List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();



    public Example(String n){

        this.name = n;

    }

 

    /**

    * Sets a new name and notifies any listeners of the change. 

    * @param The new name.

    */   

    public void setName(String name){

        String old = this.name;

        this.name = name;

        PropertyChangeEvent e = new PropertyChangeEvent(this, "Name Change", old, this.name);

        for ( PropertyChangeListener l : listeners ){

           l.propertyChanged(e);

        }

    }



    /**

    * Retrieves the name

    * @return name

    */

    public String getName(){

        return name;

    }

    /**

    * Registers a new listener for this object. 

    * @param l The listener

    */

    public void addListener(PropertyChangeListener l){

        listeners.add(l);

    }



    private void readObject(ObjectInputStream in) throws IOException{

        in.defaultReadObject();

        listeners = new ArrayList<PropertyChangeListener>();

    }

}

The above is a simple example for demonstration purposes. This class stores a name value, which can be edited via the setName() method. Any listeners registered will be notified upon change of the name. In some contexts however, one may not wish to serialize the listeners - thus the List containing the listeners is marked as transient. Without customization of the serialization however, once de-serialized any call to setName will throw a NullPointerException, as the listeners variable will default to null. The readObject method overcomes this limitation by instantiating the List.

Links





Comments

  • Priya Arrora   -   October, 5, 2017

    Serialization works across platforms - an object serialized on Solaris can be read on Windows. Very Informative article .

Back to Articles


© 2008-2022 Greg Cope