Is Custom Enum Serializable Too

Is custom enum Serializable too?

The reason it works is that serialization process for Enum's is different from serialization process for other classes. From the official documentation:

1.12 Serialization of Enum Constants

Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant's enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream.

That means, all your custom fields won't be serialized. In your case everything works well because your application process is still running and you are getting the same Enum instance that you passed to savedInstanceState.putSerializable.

But imagine a situation where your app get killed because Android has not enough memory. The next time user opens the app you will get a new Enum instance and all custom fields will have been lost and reinitialized by the constructor. Thus, mutable fields in an enum are always effectively transient.

why default serialization is prevented in enum class

Enumeration values are serializable -- and that's all that is claimed by implementing the Serializable interface.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Dummy {

public enum Color {
RED, GREEN, BLUE
}

public static void main(String[] args) throws Exception {

System.out.println(deserialize(serialize(Color.GREEN), Color.class));
}

private static <T> T deserialize(byte[] data, Class<T> cls) throws IOException, ClassNotFoundException {

try (final ByteArrayInputStream stream = new ByteArrayInputStream(data);
final ObjectInputStream reader = new ObjectInputStream(stream)) {

return cls.cast(reader.readObject());
}
}

private static byte[] serialize(Serializable obj) throws IOException {

final ByteArrayOutputStream stream = new ByteArrayOutputStream();

try {

try (final ObjectOutputStream writer = new ObjectOutputStream(stream)) {

writer.writeObject(obj);
}

} finally {

stream.close();
}

return stream.toByteArray();
}
}

However, due to the special nature of enumeration constants, the run-time cannot use the default serialization mechanism, as that would allow the construction of multiple values of a given constant, violating the guarantee, that enumeration constants are singletons.

For this reason, the run-time uses a "non-default" mechanism to serialize/deserialize enumeration constants, which ensures the singleton property. Disarming the default mechanism by overriding readObject is simply a security measure, which makes sure, that you cannot trick the VM into creating new constants by having it deserialize a maliciously hacked byte stream.

TL;DR Enumeration constants are perfectly (de)serializable as claimed by having the base class Enum implement the Serializable interface. However, internally, the VM/run-time handles the serialization specially, and disables the default mechanism for these kinds of objects.

Are there good alternatives for serializing enums in Java?

After going back and forth regarding different solutions, I figured a solution based on the suggestion from @GuiSim : one can build a class that contains an enum value. This class can

  1. do custom deserialization; thus I can prevent there won't be exceptions during the deserialization process
  2. provide simple methods like isValid() and getEnumValue(): the first one tells you if the enum deserialization actually worked; and the second one returns the deserialized enum (or throws an exception)

Custom fields on java enum not getting serialized

If the values are constant, this is better and you don't need to serialize anything

public enum ExecutionMode {
TYPE_A(x,t),
TYPE_B(y,z),
TYPE_C(b,s)

private boolean incremental; //has get/set
private String someStr; //has get/set

ExecutionMode(boolean incremental,String someStr){
///... set things appropriately
}
}

If you're setting these values at runtime, my inclination would be that this shouldn't be an enum in the first place - there should be a separate POJO that perhaps contains the values as well as a reference to an enum value.

c# Newtonsoft.json custom Enum serialization

I've been in the very same issue and developed a nuget package named StringTypeEnumConverter, that solves it.
You can check the project here.
The usage will be as simple as any other converter.

This converter derives from an already existing "StringEnumConverter", that writes the string value of the enum, instead of its numeric counterpart.
I added the support to writing the type name too.

The result will be like: "StringSplitOptions.None" (this is a string value, not an object).

Note that this converter should be applied for both writing and reading, as the resulting json would not be compatible with other readers not including this converter.

You should consider using this package only if you cannot avoid using enums in your model.
I would also suggest you to spend time to check if (custom) enums could be transformed to classes.

J.

FindBugs not complaining on non-serializable fields in enums

On one hand, the answer to your question is it’s safe to have non-transient non-serializable fields in an enum. On the other, they don’t get serialized when the enum does. So even if they are not declared transient, they work sonewhat like transient fields nevertheless.

From the Java Object Serialization Specification:

1.12 Serialization of Enum Constants
Enum constants are serialized differently than ordinary serializable or externalizable objects. The
serialized form of an enum constant consists solely of its name; field
values of the constant are not present in the form. To serialize an
enum constant, ObjectOutputStream writes the value returned by the
enum constant's name method. To deserialize an enum constant,
ObjectInputStream reads the constant name from the stream; the
deserialized constant is then obtained by calling the
java.lang.Enum.valueOf method, passing the constant's enum type along
with the received constant name as arguments. Like other serializable
or externalizable objects, enum constants can function as the targets
of back references appearing subsequently in the serialization stream.

(http://docs.oracle.com/javase/1.5.0/docs/guide/serialization/spec/serial-arch.html#enum)

So whether NonSerializable was serializable or not wouldn’t really make a difference. So FindBugs is correct in not flagging this in an enum.

If I may guess, fields in an enum are very often effectively final and initialized when the enum is loaded by the classloader, so maybe they figured that there was no point in serializing them, they can be just as well initialized again next time the enum is loaded by the classloader. It’s not something I know.

Edit: With thanks to @TJR for the link, the following SpotBugs issue is related (and links to your question): False positive SE_BAD_FIELD for non-transient non-serializable in an enum. SpotBugs is the successor of FindBugs built on the FindBugs code base and further developed.

Serializing enums with Jackson

Finally I found solution myself.

I had to annotate enum with @JsonSerialize(using = OrderTypeSerializer.class) and implement custom serializer:

public class OrderTypeSerializer extends JsonSerializer<OrderType> {

@Override
public void serialize(OrderType value, JsonGenerator generator,
SerializerProvider provider) throws IOException,
JsonProcessingException {

generator.writeStartObject();
generator.writeFieldName("id");
generator.writeNumber(value.getId());
generator.writeFieldName("name");
generator.writeString(value.getName());
generator.writeEndObject();
}
}


Related Topics



Leave a reply



Submit