Custom Serialization
Home ] Up ] Preventing Serialization ] [ Custom Serialization ]

 

 

Java's serialization mechanism provides a mechanism for classes to specify how they wish the serialization to proceed. You can:

  • supplement the default serialization, or
  • override it entirely

You can specify that a class perform custom serialization by defining methods with the signatures:

private void writeObject(ObjectOutputStream out)
     throws IOException

and:

private void readObject(ObjectInputStream in)
     throws IOException,
            ClassNotFoundException

Note: These methods must be declared private, and are not declared in any interface (interfaces can only declare public methods).

If you wish to supplement the default serialization, you must ensure that when you implement these methods, they call the default serialization methods:

private void writeObject(ObjectOutputStream out)
     throws IOException
{
    // ...
    out.defaultWriteObject();
    // ...
}
private void readObject(ObjectInputStream in)
     throws IOException,
            ClassNotFoundException
{
    // ...
    in.defaultReadObject();
    // ...
}

So let's modify the RecordedPerson class to augment the default serialization on input:

package serialization;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;

public class RecordedPerson 
    extends Person
{
    // Constructor to create a recorded person and 
    // the associated record file
    public RecordedPerson(String name, int age, int id)
    {
        super(name, age);
        m_id = id;
        createRecordFile();
    }
    
    // Constructor to create a recorded person
    // and to open an already existing record file.
    public RecordedPerson(int id)
    {
        super(null, -1);
        m_id = id;
        setRecordFile();
        setBasicData();
    }
    
    // Reads the record from the record file
    // and returns it as a string.
    public String getRecord()
    {
        String data = "";
        BufferedReader in = null;
        try
        {
            in = new BufferedReader(
                         new FileReader(m_recordFile));
            while(true)
            {
                String line = in.readLine();
                if (line == null)
                    break;
                data += line + "\n";
            }
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (in != null)
            {
                try
                {
                    in.close();
                }
                catch(IOException e)
                {}
            }
        }
        return data;
    }
    
    // Adds a new item to the record file for this person
    public void addRecord(String item)
    {
        PrintWriter out = null;
        try
        {
            out = new PrintWriter(
                        new FileWriter(
                                m_recordFile.getAbsolutePath(),
                                true));
            out.println(item);
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (out != null)
                out.close();
        }
    }
    
    // Creates a new record file with name and age record
    // (If the record file already exists, assumes that it's valid)
    private void createRecordFile()
    {
        setRecordFile();
        if (!m_recordFile.exists())
        {
            // Create a new record file
            PrintWriter out = null;
            try
            {
                out = new PrintWriter(
                                new FileWriter(m_recordFile));
                out.println(getName() + "," + getAge());
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (out != null)
                    out.close();
            }
        }
        else
        {
            // Assume for simplicity that the name and age
            // actually correspond with the current ones.
        }
    }
    
    // Sets the record File object
    // If necessary, creates the subdirectory for record files.
    private void setRecordFile()
    {
        File records = new File("records");
        if (!records.exists())
            records.mkdir();    // Create directory
        m_recordFile = new File(records, "r" + m_id + ".rec");
    }
    
    // Reads the record file and extracts the name and age
    // and sets those values for the current recorded person.
    private void setBasicData()
    {
        // Extract the name and age from the record
        String record = getRecord();
        int commaIndex = record.indexOf(',');
        int eolIndex = record.indexOf('\n');
        String name = record.substring(0, commaIndex);
        String age = record.substring(commaIndex+1, eolIndex);
        // Set the name and age values
        setName(name);
        setAge(Integer.parseInt(age));
    }
    
    // Creates a string containing RecordedPerson information.
    public String toString()
    {
        String s = super.toString();
        s += ", ID=" + m_id + ", recordFile=" + m_recordFile;
        return s;
    }
    
    private void readObject(ObjectInputStream in)
        throws IOException,
                ClassNotFoundException
    {
        in.defaultReadObject();
        // Ensure that the recordFile is properly set
        setRecordFile();
    }
    
    //// Data ////
    private int   m_id;
    private transient File  m_recordFile;
}

Which produces the proper output on serialization back in:

Adding Items to the Serialized Class

If desired, you can implement your writeObject() method in a way similar to:

private void writeObject(ObjectOutputStream out)
     throws IOException
{
    out.defaultWriteObject();
    out.writeObject(myObject);  // write out additional object
}

However, if you do this, then you must also do the equivalent when serializing back in:

private void readObject(ObjectInputStream in)
     throws IOException,
            ClassNotFoundException
{
    in.defaultReadObject();
    myObject = in.readObject(); // read in additional object
}
 
The page was last updated February 19, 2008