JSON Serialization - How to Preserve the Type with Jackson

Learn how to preserve type information during JSON serialization and deserialization using Jackson. This guide explains best practices, including the use of @JsonTypeInfo and enabling default typing, to maintain object structure while working with polymorphic types in Java.

When working with JSON serialization, especially in Java, preserving the original object type can be crucial. Jackson, a widely-used library for handling JSON in Java, provides powerful tools to manage type preservation while serializing objects. In this blog post, we'll explore how to use Jackson to serialize objects while maintaining their type information using a simpler approach with enableDefaultTyping.


Jackson is a flexible library that offers easy serialization and deserialization of Java objects to and from JSON. However, when dealing with polymorphic types, it can be tricky to ensure that the type information is preserved in the serialized JSON.

Using enableDefaultTyping with Jackson

Jackson provides a simple way to enable type information preservation using enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL).

Example:
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonSerializationExample {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        Animal dog = new Dog("Buddy", "Golden Retriever");
        Animal cat = new Cat("Whiskers", "Black");

        // Serializing
        String dogJson = objectMapper.writeValueAsString(dog);
        String catJson = objectMapper.writeValueAsString(cat);

        System.out.println("Dog JSON: " + dogJson);
        System.out.println("Cat JSON: " + catJson);

        // Deserializing
        Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);
        Animal deserializedCat = objectMapper.readValue(catJson, Animal.class);

        System.out.println("Deserialized Dog: " + deserializedDog);
        System.out.println("Deserialized Cat: " + deserializedCat);
    }
}

In this approach:

  • enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL) enables default typing for non-final classes, preserving type information during serialization.
  • During deserialization, Jackson maps the JSON back to the correct subclass of Animal based on the preserved type.

Class Hierarchies with enableDefaultTyping

  • ObjectMapper.DefaultTyping.NON_FINAL: Preserves type information for non-final classes.
  • ObjectMapper.DefaultTyping.NON_EMPTY: Preserves type for non-empty properties.
  • ObjectMapper.DefaultTyping.EVERYTHING: Preserves type information for all classes, but generally used cautiously due to performance concerns.

Conclusion

Using enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL) simplifies the process of preserving type information during JSON serialization with Jackson. This method ensures that polymorphic types are correctly serialized and deserialized while maintaining type accuracy.

This approach is especially useful when working with complex class hierarchies where multiple types are involved.