Introduction to ObjectMappers: using Jackson in the wild
Tags: java, webdev, mappers, serialization
Published: 2019-09-22 22:04:30

**Introduction**

This post is motivated by some recent real-world work I had to do involving the [Jackson ObjectMapper](https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html) and in it I'll try to give an explanation as to why it matters as well as some of its cool tricks and quirks. I'll also enumerate some of its real-world use scenarios.

**What is Jackson ObjectMapper**

Quoting the documentation:

> ObjectMapper provides functionality for reading and writing JSON, either to and from basic POJOs (Plain Old Java Objects), or to and from a general-purpose JSON Tree Model (JsonNode), as well as related functionality for performing conversions. It is also highly customizable to work both with different styles of JSON content, and to support more advanced Object concepts such as polymorphism and Object identity. ObjectMapper also acts as a factory for more advanced ObjectReader and ObjectWriter classes. Mapper (and ObjectReaders, ObjectWriters it constructs) will use instances of JsonParser and JsonGenerator for implementing actual reading/writing of JSON. Note that although most read and write methods are exposed through this class, some of the functionality is only exposed via ObjectReader and ObjectWriter: specifically, reading/writing of longer sequences of values is only available through ObjectReader.readValues(InputStream) and ObjectWriter.writeValues(OutputStream).

That's a lot to process, but, the key takeaway is right at the beginning:

**ObjectMapper provides functionality for reading and writing JSON, either to and from basic POJOs (Plain Old Java Objects), or to and from a general-purpose JSON Tree Model (JsonNode), as well as related functionality for performing conversions.**

This is very powerful, for several reasons:

- REST APIs usually return JSON responses from their endpoints, and there are multiple libraries to extract information from JSON
- JSON is a standard format that's agreed upon, universal and can be passed around as Strings, read by standard text editors, etc. In other words: it's simple, versatile and very very useful to work with and understand

The power of abstracting away from the Java representation of these objects, is that it encapsulates them and doesn't expose them when exchanging information about them directly, which is good.

As an example, a web front-end needs only to concern with a textual representation of the domain model that it's consuming in order to display it to the end users. JSON is the perfect format for the job.

**An example domain model**

Let's see how the ObjectMapper works with simple examples from a nice domain: a restaurant, a food truck, chefs and good food :)

Our restaurant will have daily food menus, chefs preparing those menus and customers visiting the restaurant. Each of these entities can be represented by the following Java classes:

```java
public class FoodtruckOwner implements Chef {

    private final String name;
    private MainDish mainDish;
    private String establishment;

    public FoodtruckOwner(String name, MainDish mainDish, String establishmentType){
        this.name = name;
        this.mainDish = mainDish;
        this.establishment=establishmentType;
    }

    public void setChefsMainDish(MainDish mainDish) {
        this.mainDish=mainDish;
    }

    public MainDish getChefsMainDish() {
        return mainDish;
    }

    public String getEstablishmentType() {
        return establishment;
    }

    public void setEstablishmentType(String typeOfEstablishment) {
        this.establishment=typeOfEstablishment;
    }
}
```

```java
public class RestaurantChef implements Chef{

    private final String name;
    private MainDish mainDish;
    private String establishment;
    private final int michelinStars;

    public RestaurantChef(String name, MainDish mainDish, String establishmentType,int michelinStars){
        this.name = name;
        this.mainDish = mainDish;
        this.establishment=establishmentType;
        this.michelinStars=michelinStars;
    }

    public void setChefsMainDish(MainDish mainDish) {
        this.mainDish=mainDish;
    }

    public MainDish getChefsMainDish() {
        return mainDish;
    }

    public String getEstablishmentType() {
        return establishment;
    }

    public void setEstablishmentType(String typeOfEstablishment) {
        this.establishment=typeOfEstablishment;
    }
}
```
Then we have the `FoodEstablishment` class:

```java
//Can be a restaurant or a food truck
public class FoodEstablishment {
    private final List<Chef> chefs;
    private final int lotation;
    private final String type;
    
    public FoodEstablishment(List<Chef> chefs, int lotation, String type){
        this.chefs = chefs;
        this.lotation = lotation;
        this.type = type;
    }

    public int getLotation() {
        return lotation;
    }

    public List<Chef> getChefs() {
        return chefs;
    }
   
    public String getType() {
        return type;
    }
}
```

And the `Chef` interface:

```java
public interface Chef {
     void setChefsMainDish(MainDish mainDish);
     MainDish getChefsMainDish();
     String getEstablishmentType();
     void setEstablishmentType(String typeOfEstablishment);
}
```

Let's go over this small domain to start with.

We have a `Cook` interface that gets implemented by both the `RestaurantChef` and `FoodtruckOwner` since both of them will have a main dish they specialize at and both of them know exactly the type of establishment they own ( duh :D ).

We also have a more general class representing a `FoodEstablishment` which can be of course, one of either, a restaurant or a food truck.

For the purpose of understanding the Jackson ObjectMapper and its inner workings, note that we have the `chefs` attribute in the `FoodEstablishment` class which is of type "list of `Chef` interface", so, a collection parametrized by an interface. This will be important later on, when we will focus on **deserialization**.

**Diving in: Serialization and Deserialization using Jackson**

Now that we introduced our small domain for learning about Jackson, let's dive into some details.

*Serialization*

Serialization is the process of writing a Java Object into JSON format (on the web you find references to POJOs (Plain Old Java Objects) but, basically, you only need a class that you want to serialize in your domain to follow some properties and anything can be serialized.

Let's look at exercising a small unit test to serialize to a JSON file the `FoodtruckOwner` class:

```java
class FoodtruckOwnerMapperTest {

    ObjectMapper mapper = new ObjectMapper();
    @Test
    public void serialization() throws IOException {
        FoodtruckOwner owner = new FoodtruckOwner("John", new MainDish("rice",6.7),"foodTruck");
        File jsonOwner = new File("/home/bruno/IdeaProjects/objectMapperJackson/owner.json");
        mapper.writeValue(jsonOwner, owner);
    }
}
```

To see how serialization works, first an instance of the class we want to serialize is created, and a file to store the serialized version is created as well.

Then, we invoke the `writeValue` method of the mapper class, which can throw an exception, that we set to be thrown in the test method itself, so we can see when things are failing and why. The mapper can throw an exception because the serialization might fail at times.

If we run the test, it will pass, and if we inspect the file contents of the `owner.json` file, here's what we have:

```
{"chefsMainDish":{"name":"rice","calories":6.7},"establishmentType":"foodTruck"}
```

You know now how to serialize a class from Java into JSON!

However, some important points:

- The name of the cook is "John" but it is not in the JSON file. 
- The "keys" in the JSON file are set from the names of the getter methods in the class.

**Annotations and ObjectMapper requirements**

The name of the cook is not in the serialized version, **because we do not have a getter for that property**. So, in order to be able to serialize attributes, we need to have getters for the properties we want to serialize. If we add the missing getter to the class, even with a more specific name:

```java
 public String getFoodtruckOwnerName() {
        return name;
    }
```

Then our serialized version is:

```
{"chefsMainDish":{"name":"rice","calories":6.7},"establishmentType":"foodTruck","foodtruckOwnerName":"John"}
```

So, regarding serialization we learned:

- We need getters for the properties that we want to be serialized.
- The actual method name of the getter is used in the JSON as a key to identify the attribute.

**Using annotations to configure the serialization**

 We can use annotations that allow us to configure the serialization. For instance, "foodtruckOwnerName" seems quite verbose, maybe we want to just serialize the attribute with the key "name".

For that, we can use the annotation `@JsonProperty` **on the getter method for the attribute** that receives as argument the new name for the serialization, so, like this:

```java
@JsonProperty("nameOfCook")
    public String getFoodtruckOwnerName() {
        return name;
    }
```

we get the following JSON:

```
{"chefsMainDish":{"name":"rice","calories":6.7},"establishmentType":"foodTruck","nameOfCook":"John"}
```

The last property we can be interested in look at, is for instance, to not serialize fields with null values, say, we receive a class instance where someone forgot to set the name of the cook, so it's null. We get then this JSON:

```
{"chefsMainDish":{"name":"rice","calories":6.7},"establishmentType":"foodTruck","nameOfCook":null}

```

If we don't want to have the field with null value included in our JSON, we can do two things:

1. Use the `@JsonInclude(JsonInclude.Include.NON_NULL)` annotation at the class level, which will ignore all the fields with a null value in the class.
2. Use the same annotation at field level, which will ignore only specific null valued fields.

And for this tutorial, this will be it regarding **serialization**. Onto **deserialization**.

*Deserialization*

**Deserialization with Jackson**

Now that we've covered the basics of serialization, it's time to move onto the reverse process, the **deserialization**.

When deserializing, we receive as input a JSON file, and we map it into a Java Object of the corresponding class we want to deserialize it into. Using the mapper, this is how it looks:

```java
@Test
    void deserialization() throws IOException {
        FoodtruckOwner owner = new FoodtruckOwner("John", new MainDish("rice",6.7),"foodTruck");
        File jsonOwner = new File("/home/bruno/IdeaProjects/objectMapperJackson/owner.json");
        mapper.writeValue(jsonOwner, owner);
        FoodtruckOwner chef = mapper.readValue(new File("/home/bruno/IdeaProjects/objectMapperJackson/owner.json"),
                FoodtruckOwner.class);
    }
```

So, we do the serialization as above, and we try to deserialize it from JSON back into our domain model class, the `FoodtruckOwner`. However, we get this error:

```
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `objectmapper.domain.FoodtruckOwner` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (File); line: 1, column: 2]

```

This is somewhat unexpected. After correctly managing to serialize the class into JSON, we run into unforeseen troubles when reading it back into Java.

Jackson requires that there's an empty default constructor on the target class, so, if we have this now:

```java
 FoodtruckOwner(){

    }

    public FoodtruckOwner(String name, MainDish mainDish, String establishmentType){
        this.name = name;
        this.mainDish = mainDish;
        this.establishment=establishmentType;
    }
```

it will surely work, right?

Not quite, it turns out that we had the same missing default constructor in our `MainDish` class... This is starting to get complex. Let's add the default constructor to our `MainDish` class.

After adding the default constructor to the `MainDish` class, we get what we want, the test is green and we have our instance in Java, correctly deserialized:

```java
@Test
    void deserialization() throws IOException {
        FoodtruckOwner owner = new FoodtruckOwner("John", new MainDish("rice",6.7),"foodTruck");
        File jsonOwner = new File("/home/bruno/IdeaProjects/objectMapperJackson/owner.json");
        mapper.writeValue(jsonOwner, owner);
        FoodtruckOwner chef = mapper.readValue(new File("/home/bruno/IdeaProjects/objectMapperJackson/owner.json"),
                FoodtruckOwner.class);
        assertEquals("John", chef.getName());
        final MainDish chefsMainDish = chef.getChefsMainDish();
        assertNotNull(chefsMainDish);
        assertEquals("rice", chefsMainDish.getName());
        assertEquals(6.7, chefsMainDish.getCalories());

    }
```

So, we see that, **after adding default constructors**, our deserialization process worked and we managed to correctly read in our JSON representing the food truck owner back into Java. However there were some issues. 

It was necessary to modify the Java classes in quite intrusive ways, since we needed to explicitly add default constructors to these classes that are mere "clients" from the perspective of the deserialization process. This is not good.
What if the `MainDish` class was actually just a library to which we didn't have direct access to? Then these modifications in the source code would be impractical if not at all impossible (not impossible, tools like "asm inspector" in IntelliJ can effectively decompile libraries into source code that you can copy and adapt to your needs, but, this is not the way).

Let's assume that such scenario is what we are facing: we can't modify the source of `MainDish`. How to approach it then?

**Jackson MixIns**

When we can't modify the source code directly to adhere to the conventions imposed by Jackson, we need to resort to some special functionalities that allow Jackson to interact "indirectly" with 3rd party code. 
One such functionality is called ["Mix-Ins"](https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations).

"Mix-in" annotations are a way to associate annotations with classes, without modifying (target) classes themselves, originally intended to help support 3rd party datatypes where user can not modify sources to add annotations.

With mix-ins you can:

- Define that annotations of a '''mix-in class''' (or interface)
will be used with a '''target class''' (or interface) such that it appears
as if the ''target class'' had all annotations that the ''mix-in'' class has (for purposes of configuring serialization / deserialization)
You can think of it as kind of aspect-oriented way of adding more annotations during runtime, to augment statically defined ones.

This is exactly what is needed for our case, since we are unable to modify the `MainDish` class.

We can use the following mix-in:

```java
public abstract class MainDishMixin extends MainDish {
    @JsonCreator
    public MainDishMixin(@JsonProperty("name") String name, @JsonProperty("calories") double calories) {
        super(name, calories);
    }
}
```

Here we note some important things:

- The mix-in must be defined in a different package than the target class, and it **can not be defined as an inner class of the target**

- The parameters that emulate the original constructor of the class are annotated with the `@JsonProperty` annotation and the same names **and types** as in the target class

Then we register the MixIn in our ObjectMapper class using ` mapper.addMixIn(MainDish.class, MainDishMixin.class);` and we are done!

Then the test will pass and we achieved what we wanted: without directly modifying the target class, we managed to successfully deserialize it into a Java object.

**Deserializing interface fields in classes**

Our last feature will be how to deserialize fields that are parametrized as interfaces in our code. Let's assume that we added all getters and setters to the classes `RestaurantChef` and `FoodtruckOwner`, serialized it and now have this JSON in our hands:

```
{"chefs":[{"name":"Michel","michelinStars":4,"chefsMainDish":{"name":"Chicken","calories":20.0},"establishmentType":"Restaurant"},{"name":"John","chefsMainDish":{"name":"rice","calories":6.7},"establishmentType":"foodTruck"}],"lotation":10}
```

Let's assume for this last example we have this class:

```java
//Can be a restaurant or a food truck
public class FoodEstablishment {
    private List<Chef> chefs;
    private int lotation;

    public FoodEstablishment(){}

    public FoodEstablishment(List<Chef> chefs, int lotation){
        this.chefs = chefs;
        this.lotation = lotation;
    }

    public int getLotation() {
        return lotation;
    }

    public List<Chef> getChefs() {
        return chefs;
    }
}
```

Unfortunately, attempting to deserialize this class as is, will bring us to yet one more error:

```
DefinitionException: Cannot construct instance of `objectmapper.domain.Chef` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (File); line: 1, column: 11] (through reference chain: objectmapper.domain.FoodEstablishment["chefs"]->java.util.ArrayList[0])
```

What this means is that since `Chef` is being used as an interface there, Jackson won't know how to deserialize it, because the interface is actually shared by two implementations. To make this work we need to annotate the interface with additional information regarding the different sub-types:

```java
@JsonTypeInfo(
        use = JsonTypeInfo.Id.CLASS,
        include = JsonTypeInfo.As.PROPERTY,
        property = "class")
@JsonSubTypes({
        @JsonSubTypes.Type(value = RestaurantChef.class, name = "restaurantChef"),
        @JsonSubTypes.Type(value = FoodtruckOwner.class, name = "foodtruckOwner")})
     public interface Chef {
     void setChefsMainDish(MainDish mainDish);
     MainDish getChefsMainDish();
     String getEstablishmentType();
     void setEstablishmentType(String typeOfEstablishment);
}
```

After adding the above annotation, the information provided by it, allows Jackson to deduce when deserializing which implementation to use, and everything will work!

**Conclusions**

I hope this post provided a good overall introduction to the process of serialization and deserialization in Java while using Jackson and that it'll allow to explore even more from here on. Thanks for reading!