1. Overview
In this tutorial, we’re going to take a look at inheritance and in particular how to handle JSON serialization and deserialization of Java classes that extend a superclass.
In order to get started with Jackson, you can have a look at this article here.
2. JSON Inheritance – Real World Scenario
Let’s say that we have an abstract Event class which is used for some sort of event processing:
abstract public class Event { private String id; private Long timestamp; // standard constructors, getters/setters }
There are two subclasses that extend the Event class, the first is ItemIdAddedToUser, and the second is ItemIdRemovedFromUser.
The problem is that if we want to represent these classes as JSON, we would need to serialize their type information in addition to their ordinary fields. Otherwise, when deserializing into an Event there would be no way of knowing what subclass the JSON represents. Fortunately, there is a mechanism to achieve this in the Jackson library.
3. Implementing Inheritance Using Jackson
There is a @JsonTypeInfo annotation that allows us to store an object’s type information as a JSON field.
Let’s enrich the Event class with the annotation:
@JsonTypeInfo( use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "eventType") abstract public class Event { private String id; private Long timestamp; @JsonCreator public Event( @JsonProperty("id") String id, @JsonProperty("timestamp") Long timestamp) { this.id = id; this.timestamp = timestamp; } // standard getters }
In this case, using the annotation will cause the ObjectMapper to add an additional field called eventType to the resulting JSON, with a value equal to the object’s type. By using JsonTypeInfo.Id. MINIMAL_CLASS, it means that the value of the eventType property will be equal to the name of the class. Let’s serialize an instance of an ItemIdRemovedFromUserEvent:
Event event = new ItemIdRemovedFromUser("1", 12345567L, "item_1", 2L); ObjectMapper objectMapper = new ObjectMapper(); String eventJson = objectMapper.writeValueAsString(event);
When we print the JSON we will now see that the additional type information is stored:
{ "eventType":".ItemIdRemovedFromUser", "id":"1", "timestamp":12345567, "itemId":"item_1", "quantity":2 }
Let’s validate that the deserialization works by asserting that the ObjectMapper creates an instance of an ItemIdRemovedFromUser class:
@Test public void givenRemoveItemJson_whenDeserialize_shouldHaveProperClassType() throws IOException { //given Event event = new ItemIdRemovedFromUser("1", 12345567L, "item_1", 2L); ObjectMapper objectMapper = new ObjectMapper(); String eventJson = objectMapper.writeValueAsString(event); //when Event result = new ObjectMapper().readValue(eventJson, Event.class); //then assertTrue(result instanceof ItemIdRemovedFromUser); assertEquals("item_1", ((ItemIdRemovedFromUser) result).getItemId()); }
6. Ignoring Fields From a Super-Class
Let’s say that we want to extend an Event class, but we want our ObjectMapper to ignore it’s id field so it is not present in the resulting JSON.
It’s quite easily achieve this by using the @JsonIgnoreProperties annotation:
@JsonIgnoreProperties("id") public class ItemIdAddedToUser extends Event { private String itemId; private Long quantity; @JsonCreator public ItemIdAddedToUser( @JsonProperty("id") String id, @JsonProperty("timestamp") Long timestamp, @JsonProperty("itemId") String itemId, @JsonProperty("quantity") Long quantity) { super(id, timestamp); this.itemId = itemId; this.quantity = quantity; } // standard getters }
Let’s serialize an ItemAddedToUserEvent, and see which fields are ignored by the ObjectMapper:
Event event = new ItemIdAddedToUser("1", 12345567L, "item_1", 2L); ObjectMapper objectMapper = new ObjectMapper(); String eventJson = objectMapper.writeValueAsString(event);
The resulting JSON will look like this (note that there is no id field from Event super-class):
{ "eventType":".ItemIdAddedToUser", "timestamp":12345567, "itemId":"item_1", "quantity":2 }
Let’s assert that the id field is missing with a test case:
@Test public void givenAdddItemJson_whenSerialize_shouldIgnoreIdPropertyFromSuperclass() throws IOException { // given Event event = new ItemIdAddedToUser("1", 12345567L, "item_1", 2L); ObjectMapper objectMapper = new ObjectMapper(); // when String eventJson = objectMapper.writeValueAsString(event); // then assertFalse(eventJson.contains("id")); }
7. Conclusion
This article demonstrates how to use inheritance with Jackson library.
The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.