Explain Codes LogoExplain Codes Logo

Jackson Enum Serializing and Deserializing

java
enum-serializing
jackson-serialization
custom-serializers
Alex KataevbyAlex Kataev·Sep 4, 2024
TLDR

To implement enum serialization and deserialization with Jackson, use the @JsonValue and @JsonCreator annotations, respectively. The @JsonValue should be annotated on the getter method providing the serialized value in the enum, while the @JsonCreator should be on a static method that reverts the serialized value back to an enum constant:

public enum Type { // It was "yes" or "no" question, but of course we made it harder... YES("Y"), NO("N"); private String code; Type(String code) { this.code = code; } // Jackson, do your magic! @JsonValue public String getCode() { return code; } // Jackson, reverse the spell! @JsonCreator public static Type fromCode(String code) { // Enum magic happening here... return Stream.of(Type.values()) .filter(c -> c.code.equals(code)) .findFirst() // Oops! The spell failed... .orElseThrow(() -> new IllegalStateException("Invalid code: " + code)); } }

Running this will serialize Type.YES to "Y", and deserialize "Y" back to Type.YES.

Case Insensitivity, Failures, and More!

How about when your enums have complex mappings, or when the JSON input may not stick to case conventions? No worry! You can create a Map and implement a method for case-insensitive deserialization.

public static Type forValue(String code) { // Old McDonald had a map... return stringToEnumMap.get(StringUtils.lowerCase(code)); }

Also consider handling "I don't know what this is!" scenarios during serialization. You can return null from the toValue() method, or generate an exception.

Custom Serializers/Deserializers: Just Do It Your Way!

Need even more control over your serialization/deserialization process? Consider utilizing custom serializer/deserializer classes. This will allow you more complex logic and better flexibility handling non-standard enum representations.

public class MySerializer extends StdSerializer<MyEnum> { public MySerializer() { super(MyEnum.class); } @Override public void serialize(MyEnum value, JsonGenerator gen, SerializerProvider provider) throws IOException { // Bippity Boppity Boo! gen.writeString(value.getCustomString()); } }

Also, remember in Jackson 2.6 and above, you can use @JsonProperty on each constant to directly declare its serialization name.

public enum Role { // I prefer "cheese maker", but okay... @JsonProperty("Admin") ADMINISTRATOR, @JsonProperty("Janitor") SUPER_USER }

Edge Cases: The Enum Strikes Back!

When you have additional properties tied to enum constants, or you need to work with polymorphic deserialization, remember to tailor your solution in such a way that Jackson smiles and gives you the correct outputs.

You can also add a @JsonEnumDefaultValue to handle unknown or deprecated enum values gracefully. Thus, the application continues running, regardless of the string that comes knocking.

Considerations: The Good, The Bad, The Ugly

Customize ObjectMapper Settings

ObjectMapper settings can be your ultimate game changer. If you'd like to take a global approach in how your application handles enums, use WRITE/READ_ENUMS_USING_TO_STRING flag.

ObjectMapper mapper = new ObjectMapper(); // Let every enum be a string! mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);

Deserialization Logic

Ensure your logic is bulletproof. Your deserialization logic must be able to handle as many inputs as possible while maintaining the integrity of your application.

Map It Right!

Padlock your (de)serializer classes for compatibility with the Java and Jackson versions in use. You wouldn't want to throw a NoSuchMethodError party, would you?