3 ways to serialize Java Enums

This article shows you best practice for serializing Java enums to database (via Hibernate, JPA) or file. It discusses three ways to serialize enums with code examples, the pros and cons of each way and also recommends the best way. You should know this if you are serializing an Object with an enum field to a file or mapping a POJO to a database table using technologies like Hibernate or JPA.

What exactly is a Java enum constant? A Java enum is a sub-class of java.lang.Enum. An enum constant is simply an object instance of the enum class. The enum constructor must be private. Enum constants are created by the JVM via an invocation of the private constructor. Customizing the constructor signature is the most common way of adding more data to the enum class. You can add methods to the enum class just like any other class.

What does serialization and deserialization of enum mean? Serialization involves converting the enum to a value (usually primitive or a String) which can be easily stored on the disk or a database. Deserialization involves reading a stored value and converting it back to an enum. It is important to understand the different ways in which enum serialization and deserialization works. Sometimes ORM implementations (like Hibernate) may automatically do the enum serialization for you, however there are drawbacks to some of these approaches that you should know.

Let’s say we want to serialize the following enum

public enum Color {
 RED, GREEN, BLUE;
}

Following are 3 different approaches. The last approach is the most reliable way of serializing an enum class, however you should know approaches 1 and 2 so you can understand their drawbacks.

Approach 1: using the ordinal() value

Approach 2: using the name() value

Approach 3: Using a user defined business valueBest practice!

Approach 1: using the ordinal() value

Ordinal of an enum constant is the value is its position in its enum declaration. This is true for all enums. In the above example:

RED.ordinal()   == 0
GREEN.ordinal() == 1
BLUE.ordinal()  == 2

You can serialize the enum by converting the enum to its ordinal() value.

Color color = Color.GREEN;
int ordinal = color.ordinal(); // ordinal == 1
// save ordinal

At a later point, you can deserialize the saved int using the values() method on the enum which returns all the enum constants in the order in this they were declared.

// retrieve from saved value
Color savedColor = Color.values()[savedOrdinal];

This approach is simple and works. Some ORM implementations may automatically use this approach if you map your enum to and Integer column. However, the biggest drawback of this approach is that if new constants are introduced out of order then the serialized values will not be properly reconstructed.

Here’s the error scenario:

  1. Serialize an enum value Color.GREEN. Saved value gets stored as 1
  2. Developer adds a new color ORANGE between RED and GREEN
  3. The previously serialized value of 1 is now mapped to ORANGE.

Approach 2: using the name() value

The name() of any enum is the literal that is used to represent the enum constant in the Java program.

RED.name().equals("RED")
GREEN.name().equals("GREEN")
BLUE.name().equals("BLUE")

Serialize as follows:

Color color = Color.GREEN;
String savedValue = color.name();
// save value

Deserialize as follows:

Color savedColor = Color.valueOf(savedValue);

valueOf() is a built-in method which returns the enum constant with the specified specified name

This method works as well. Some ORM implementations may automatically use this approach if you map your enum to and String (char, varchar) column.The serialized form is String (as opposed to int in the previous example). The main drawback of this approach is that the serialized value, which can then be stored on disk or in database and can be persisted for a long term, is now dependent on a Java constant name. A developer may accidentally rename the constant name without realizing that it will make the previously serialized values unreadable.

Here’s the error scenario:

  1. Serialize an enum value Color.GREEN. Saved value gets stored as “GREEN”
  2. Developer renamed enum constant from Color.GREEN to Color.Green.
  3. The previously serialized value of “GREEN” can no longer be mapped to an existing color.

Approach 3: Using a user defined business value – Recommended approach!

This approach involves assigning a an explicit user defined value to each enum constant and defining a toValue() and fromValue() methods on the enum to do the serialization and deserialization.

public enum Color {
 RED("RED"), GREEN("GREEN"), BLUE("BLUE"), UNKNOWN("UNKNOWN");

 private final String value;

 Color(String value) {
   this.value = value;
 }

 public static Color fromValue(String value) {
   if (value != null) {
     for (Color color : values()) {
       if (color.value.equals(value)) {
         return color;
       }
     }
   }

   // you may return a default value
   return getDefault();
   // or throw an exception
   // throw new IllegalArgumentException("Invalid color: " + value);
 }

 public String toValue() {
   return value;
 }

 public static Color getDefault() {
   return UNKNOWN;
 }
}

This approach is better than approach 1 and approach 2 above. It neither depends on the order in which the enum constants are declared nor on the constant names.

To serialize:

Color color = Color.GREEN;
String savedValue = color.toValue();
// save value

To deserialize:

Color savedColor = Color.fromValue(savedValue);

Note: You can choose an Integer instead of String as a value, but remember this value must not change or you will be unable to retrieve you persisted values.

Mapping enum to database column using JPA/Hibernate

You can use any of the 3 approaches discussed above.

  1. Map the enum to an integer column. The persistence implementation should automatically convert enum to ordinal() and back for you.
  2. Map the enum to a String column. The persistence implementation should automatically convert the enum value to String value via the name() function.
  3. Map the enum using a business value. You should mark the enum field as @Transient, and create another String field which you can map to a String column in your database table. Here’s an example code snippet.
@Entity
public class Product {
 @Column
 private String colorValue;

 @Transient
 public Color getColor() {
  return Color.fromValue(colorValue);
 }

 public void setColor(Color color) {
  this.colorValue = color.toValue();
 }
}

Conclusion

We saw 3 different ways to serialized enums in this article. Approach 1 and 2 above use ordinal() and name() respectively, and require no additional work, but are inherently unsafe to use. The safest way is approach 3 which uses a custom user defined value as shown in the example above. When persisting enums with Hibernate/JPA first convert them to a user defined value.

Related posts:

  1. 2 ways to convert Java Map to String
  2. New Java 7 Feature: String in Switch support
  3. 3 ways to run Java main from Maven

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Get Adobe Flash playerPlugin by wpburn.com wordpress themes