DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • High-Performance Java Serialization to Different Formats
  • Did You Know the Fastest Way of Serializing a Java Field Is Not Serializing It at All?
  • What Is Ant, Really?
  • Beginners Guide for Web Scraping Using Selenium

Trending

  • What Is Good Database Design?
  • GenAI-Infused ChatGPT: A Guide To Effective Prompt Engineering
  • Spring WebFlux Retries
  • The Convergence of Testing and Observability
  1. DZone
  2. Coding
  3. Languages
  4. Serializing Java Objects with Non-Serializable Attributes

Serializing Java Objects with Non-Serializable Attributes

Dustin Marx user avatar by
Dustin Marx
·
Feb. 12, 14 · Interview
Like (1)
Save
Tweet
Share
23.19K Views

Join the DZone community and get the full member experience.

Join For Free

There are multiple reasons one might want to use custom serialization instead of relying on Java's default serialization. One of the most common reasons is for performance improvements, but another reason for writing custom serialization is when the default serialization mechanism is unsupported. Specifically, as will be demonstrated in this post, custom serialization can be used to allow a larger object to be serialized even when attributes of that object are not themselves directly serializable.

The next code listing shows a simple class for serializing a given class to a file of the provided name and for deserializing an object from that same file. I will be using it in this post to demonstrate serialization.

SerializationDemonstrator.java

view source
print?
01.package dustin.examples.serialization;
02. 
03.import static java.lang.System.out;
04. 
05.import java.io.FileInputStream;
06.import java.io.FileOutputStream;
07.import java.io.IOException;
08.import java.io.ObjectInputStream;
09.import java.io.ObjectOutputStream;
10. 
11./**
12. * Simple serialization/deserialization demonstrator.
13. *
14. * @author Dustin
15. */
16.public class SerializationDemonstrator
17.{
18.   /**
19.    * Serialize the provided object to the file of the provided name.
20.    * @param objectToSerialize Object that is to be serialized to file; it is
21.    *     best that this object have an individually overridden toString()
22.    *     implementation as that is used by this method for writing our status.
23.    * @param fileName Name of file to which object is to be serialized.
24.    * @throws IllegalArgumentException Thrown if either provided parameter is null.
25.    */
26.   public static <T> void serialize(final T objectToSerialize, final String fileName)
27.   {
28.      if (fileName == null)
29.      {
30.         throw new IllegalArgumentException(
31.            "Name of file to which to serialize object to cannot be null.");
32.      }
33.      if (objectToSerialize == null)
34.      {
35.         throw new IllegalArgumentException("Object to be serialized cannot be null.");
36.      }
37.      try (FileOutputStream fos = new FileOutputStream(fileName);
38.           ObjectOutputStream oos = new ObjectOutputStream(fos))
39.      {
40.         oos.writeObject(objectToSerialize);
41.         out.println("Serialization of Object " + objectToSerialize + " completed.");
42.      }
43.      catch (IOException ioException)
44.      {
45.         ioException.printStackTrace();
46.      }
47.   }
48. 
49.   /**
50.    * Provides an object deserialized from the file indicated by the provided
51.    * file name.
52.    *
53.    * @param <T> Type of object to be deserialized.
54.    * @param fileToDeserialize Name of file from which object is to be deserialized.
55.    * @param classBeingDeserialized Class definition of object to be deserialized
56.    *    from the file of the provided name/path; it is recommended that this
57.    *    class define its own toString() implementation as that will be used in
58.    *    this method's status output.
59.    * @return Object deserialized from provided filename as an instance of the
60.    *    provided class; may be null if something goes wrong with deserialization.
61.    * @throws IllegalArgumentException Thrown if either provided parameter is null.
62.    */
63.   public static <T> T deserialize(final String fileToDeserialize, final Class<T> classBeingDeserialized)
64.   {
65.      if (fileToDeserialize == null)
66.      {
67.         throw new IllegalArgumentException("Cannot deserialize from a null filename.");
68.      }
69.      if (classBeingDeserialized == null)
70.      {
71.         throw new IllegalArgumentException("Type of class to be deserialized cannot be null.");
72.      }
73.      T objectOut = null;
74.      try (FileInputStream fis = new FileInputStream(fileToDeserialize);
75.           ObjectInputStream ois = new ObjectInputStream(fis))
76.      {
77.         objectOut = (T) ois.readObject();
78.         out.println("Deserialization of Object " + objectOut + " is completed.");
79.      }
80.      catch (IOException | ClassNotFoundException exception)
81.      {
82.         exception.printStackTrace();
83.      }
84.      return objectOut;
85.   }
86.}

The next code listing illustrates use of the SerializationDemonstrator class to serialize and deserialize a standard Java string (which is Serializable). A screen snapshot follows the code listing and shows the output (in NetBeans) of running that String through the serialize and deserialize methods of theSerializationDemonstrator class.

Running SerializationDemonstrator Methods on String

SerializationDemonstrator.serialize("Inspired by Actual Events", "string.dat");
final String stringOut = SerializationDemonstrator.deserialize("string.dat", String.class);

The next two code listings are for the class Person.java and a class that it has as an attribute type (CityState.java). Although Person implements Serializable, the CityAndState class does not.

Person.java

package dustin.examples.serialization;

import java.io.Serializable;

/**
 * Person class.
 * 
 * @author Dustin
 */
public class Person implements Serializable
{
   private String lastName;
   private String firstName;
   private CityState cityAndState;

   public Person(
      final String newLastName, final String newFirstName,
      final CityState newCityAndState)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.cityAndState = newCityAndState;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   @Override
   public String toString()
   {
      return this.firstName + " " + this.lastName + " of " + this.cityAndState;
   }
}

CityAndState.java

package dustin.examples.serialization;

/**
 * Simple class storing city and state names that is NOT Serializable.
 * 
 * @author Dustin
 */
public class CityState
{
   private final String cityName;
   private final String stateName;

   public CityState(final String newCityName, final String newStateName)
   {
      this.cityName = newCityName;
      this.stateName = newStateName;
   }

   public String getCityName()
   {
      return this.cityName;
   }

   public String getStateName()
   {
      return this.stateName;
   }

   @Override
   public String toString()
   {
      return this.cityName + ", " + this.stateName;
   }
}

The next code listing demonstrates running SerializationDemonstrator on the serializable Person class with a non-serializable CityState. The code listing is followed by a screen snapshot of the output in NetBeans.

Running SerializationDemonstrator Methods on Serializable Person with Non-Serializable CityState

final Person personIn = new Person("Flintstone", "Fred", new CityState("Bedrock", "Cobblestone"));
SerializationDemonstrator.serialize(personIn, "person.dat");

final Person personOut = SerializationDemonstrator.deserialize("person.dat", Person.class);

In this case, the CityState class is my own class and I could make it Serializable. However, supposing that this class was part of a third-party framework or library and I was not able to change the class itself, I can change Person to use custom serialization and deserialization and work with CityState properly. This is shown in the next code listing for the class SerializablePerson that is adapted from Person.

SerializablePerson.java

package dustin.examples.serialization;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * Person class.
 * 
 * @author Dustin
 */
public class SerializablePerson implements Serializable
{
   private String lastName;
   private String firstName;
   private CityState cityAndState;

   public SerializablePerson(
      final String newLastName, final String newFirstName,
      final CityState newCityAndState)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.cityAndState = newCityAndState;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   @Override
   public String toString()
   {
      return this.firstName + " " + this.lastName + " of " + this.cityAndState;
   }

   /**
    * Serialize this instance.
    * 
    * @param out Target to which this instance is written.
    * @throws IOException Thrown if exception occurs during serialization.
    */
   private void writeObject(final ObjectOutputStream out) throws IOException
   {
      out.writeUTF(this.lastName);
      out.writeUTF(this.firstName);
      out.writeUTF(this.cityAndState.getCityName());
      out.writeUTF(this.cityAndState.getStateName());
   }
 
   /**
    * Deserialize this instance from input stream.
    * 
    * @param in Input Stream from which this instance is to be deserialized.
    * @throws IOException Thrown if error occurs in deserialization.
    * @throws ClassNotFoundException Thrown if expected class is not found.
    */
   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
   {
      this.lastName = in.readUTF();
      this.firstName = in.readUTF();
      this.cityAndState = new CityState(in.readUTF(), in.readUTF());
   }

   private void readObjectNoData() throws ObjectStreamException
   {
      throw new InvalidObjectException("Stream data required");
   }
}

The above code listing shows that SerializablePerson has custom writeObject and readObject methods to support custom serialization/deserialization that handle its attribute of unserializable type CityStateappropriately. A snippet of code for running this class through the SerializationDemonstrator and the successful output of doing so are shown next.

Running SerializationDemonstrator on SerializablePerson

final SerializablePerson personIn = new SerializablePerson("Flintstone", "Fred", new CityState("Bedrock", "Cobblestone"));
SerializationDemonstrator.serialize(personIn, "person.dat");

final SerializablePerson personOut = SerializationDemonstrator.deserialize("person.dat", SerializablePerson.class);

The approach depicted above will allow non-serializable types to be used as attributes of serializable classes without the need to make those fields transient. However, if the CityState instance shown earlier needs to be used in multiple serializable classes, it might be better to decorate  the CityState class with a serializable decorator  and then used that serialized decorator class in classes needing to be serialized. The next code listing shows SerializableCityState which decorates  CityState with a serialized version.

SerializableCityState

package dustin.examples.serialization;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * Simple class storing city and state names that IS Serializable. This class
 * decorates the non-Serializable CityState class and adds Serializability.
 * 
 * @author Dustin
 */
public class SerializableCityState implements Serializable
{
   private CityState cityState;

   public SerializableCityState(final String newCityName, final String newStateName)
   {
      this.cityState = new CityState(newCityName, newStateName);
   }

   public String getCityName()
   {
      return this.cityState.getCityName();
   }

   public String getStateName()
   {
      return this.cityState.getStateName();
   }

   @Override
   public String toString()
   {
      return this.cityState.toString();
   }

   /**
    * Serialize this instance.
    * 
    * @param out Target to which this instance is written.
    * @throws IOException Thrown if exception occurs during serialization.
    */
   private void writeObject(final ObjectOutputStream out) throws IOException
   {
      out.writeUTF(this.cityState.getCityName());
      out.writeUTF(this.cityState.getStateName());
   }
 
   /**
    * Deserialize this instance from input stream.
    * 
    * @param in Input Stream from which this instance is to be deserialized.
    * @throws IOException Thrown if error occurs in deserialization.
    * @throws ClassNotFoundException Thrown if expected class is not found.
    */
   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
   {
      this.cityState = new CityState(in.readUTF(), in.readUTF());
   }

   private void readObjectNoData() throws ObjectStreamException
   {
      throw new InvalidObjectException("Stream data required");
   }
}

This serializable decorator can be used in the Person class directly and that enclosing Person can use default serialization because its fields are all serializable. This is shown in the next code listing for Person2 adapted from Person.

Person2.java

package dustin.examples.serialization;

import java.io.Serializable;

/**
 * Person class.
 * 
 * @author Dustin
 */
public class Person2 implements Serializable
{
   private final String lastName;
   private final String firstName;
   private final SerializableCityState cityAndState;

   public Person2(
      final String newLastName, final String newFirstName,
      final SerializableCityState newCityAndState)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.cityAndState = newCityAndState;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   @Override
   public String toString()
   {
      return this.firstName + " " + this.lastName + " of " + this.cityAndState;
   }
}

This code can be executed as shown in the next code listing, which is followed by its output as seen in the NetBeans output window.

Running SerializationDemonstrator Against Person2/SerializableCityState

final Person2 personIn = new Person2("Flintstone", "Fred", new SerializableCityState("Bedrock", "Cobblestone"));
SerializationDemonstrator.serialize(personIn, "person2.dat");

final Person2 personOut = SerializationDemonstrator.deserialize("person2.dat", Person2.class);

Custom serialization can be used to allow a class with attributes of nonserializable types to be serialized without making those attributes of nonserializable type transient. This is a useful technique when serializable classes need to use attributes of types that are not serializable and that cannot be changed.

Attribute (computing) Object (computer science) Java (programming language) Listing (computer) Serialization

Published at DZone with permission of Dustin Marx, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • High-Performance Java Serialization to Different Formats
  • Did You Know the Fastest Way of Serializing a Java Field Is Not Serializing It at All?
  • What Is Ant, Really?
  • Beginners Guide for Web Scraping Using Selenium

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: