Jackson API - Object to JSON and vice versa

Jackson is a simple lightweight API to convert Java Objects to JSON and vice-versa.
I do not have statistics to vouch for it being the best and fastest, nevertheless it is very easy to learn and implement.

Here I would like to explore a few common ways in which Jackson API is used. It is not a complete reference, but could serve as a tutorial. The layout is broken as follows

Table of Content 

  1. Setup and Initialization
  2. Java Object to JSON
    • Using different field name 
    • Ignoring properties 
      • Ignoring a bunch of properties
  3. JSON to Java Object
    • Using different field name
  4. Sub classes
    • Ignoring parent property
  5. Associations
  6.  Other configurations
    • Ignoring null properties
    • Dates
    • Unknown properties
  7. Other tricks
    • Strings as raw json 
  8. Conclusion

Setup and Initialization

 The first step would be to download Jackson API and integrating to build path. The API Jars can be downloaded at Jackson download page. Yes, you need to download all the 3 JARs - core, mapper and annotations.

The conversion revolves around org.codehaus.jackson.map.ObjectMapper class. This class is responsible to convert Object to JSON and vice versa.

The only thing we need to do here is - instantiate this mapper object.

import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonUtils {
   private final static ObjectMapper mapper;
   static {
      mapper = new ObjectMapper();
   }
}

Java Object to Json

ObjectMapper is our guy to convert a Java object to JSON.
Jackson works on POJOs converting their data to JSON.

Serialization is done as
// Here object is the one we wish to serialize

String serializedObject = mapper.writeValueAsString(object) 


This will serialize every field with its field name. See the example below.

We have a POJO called Content that has two properties namely title and content.

public class Content {
   private final String title;
   private final String content;
   private final String comment;

   public Content(String title, String content, String comment) {
      this.title = title;
      this.content = content;
      this.comment = comment;
   }

   // Getters and Setters
}

Now we need to convert this object to json using our ObjectMapper. So we update JsonUtils class.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonUtils {
   private final static ObjectMapper mapper;
   static {
      mapper = new ObjectMapper();
   }

   public static String serialize(Object object) {
      try {
         return mapper.writeValueAsString(object);
      } catch (JsonProcessingException e) {
         e.printStackTrace();
      }
      return null;
   }
}

Now, its time to test our code.

public class TestSerialization {
   public static void main(String args[]) {
      Content[] contents = new Content[] {
            new Content("title 1", "content 1", "comment 1"),
            new Content("title 2", "content 2", "comment 2"),
            new Content("title 3", "content 3", "comment 3")
      };
      System.out.println(JsonUtils.serialize(contents));
   }
}

The JSON (formatted from JSON lint)response is
[
    {
        "title": "title 1",
        "content": "content 1",
        "comment": "comment 1"
    },
    {
        "title": "title 2",
        "content": "content 2",
        "comment": "comment 2"
    },
    {
        "title": "title 3",
        "content": "content 3",
        "comment": "comment 3"
    }
]

What we observe here is that the property names in Java Object (title, content and comment) have become field names in JSON. (Allow me to call java instance variable as 'property' and JSON key as 'field' for simplicity)

Note: Jackson uses getters to create field names.

Using different field name

Sometimes we may wish to use another name for the JSON field. There are 3 ways in which we can do it
  1. Change the property name
  2. Change the getter method name (because Jackson uses getter to make field name)
  3. Use annotation
I prefer using annotation because that way we do not need to touch the code. We simply do the following to change the 'title' field as 'header' in JSON

@JsonProperty("header")
private final String title;

Or
@JsonProperty("header")
public String getTitle() {
   return title;
}
The new field name is passed as @JsonProperty annotation parameter.

Note: The @JsonProperty annotation can be used both on property and on getter. 
I personally like to put the annotation over property itself.

Ignoring properties

You may sometimes be in a situation where you wouldn't like to serialize a property into JSON field. Let's say we want to ignore the comment field. So this is how we do it.

@JsonIgnore
private final String comment;

Note: The @JsonIgnore annotation can be used both on property and on getter.
If we have multiple properties we wish to ignore, then we can do the following
@JsonIgnoreProperties({"content", "comment"})
public class Content {
   ...
}

Note: The @JsonIgnoreProperties annotation can be used only on a class.

Json to Java object

ObjectMapper.java is responsible to decode JSON string into our objects. And this is how its done
I would add this method to the JsonUtil.java.

import com.fasterxml.jackson.core.type.TypeReference;

@SuppressWarnings("unchecked")
public static  T extractObjectFromJson(String jsonText, TypeReference type) {
   try {
      return (T) mapper.readValue(jsonText, type);
   } catch (JsonParseException e) {
      e.printStackTrace();
   } catch (JsonMappingException e) {
      e.printStackTrace();
   } catch (IOException e) {
      e.printStackTrace();
   }
   return null;
}

Object mapper alone cannot do the trick. To de-serialize a JSON, it needs a constructor. And since all it gets is strings from JSON, it needs to know where each string fits in constructor. So we need to modify the constructor of POJO - Content.java

public Content(@JsonProperty("header") String title, @JsonProperty("content") String content,
      @JsonProperty("comment") String comment) {
   this.title = title;
   this.content = content;
   this.comment = comment;
}

Each constructor argument is annotated with @JsonProperty indicating the field it maps to in JSON.

TypeReference is a special parameter whose instance has type reference of the object we wish the JSON to be translated to. It is used as

List<Content> contentsFromJson = JsonUtils.extractObjectFromJson(serializedContent,
   new TypeReference<List<Content>>() {
});

Using different field name (De-searilizing JSON)

As expected, we need to provided a proper field name with @JsonProperty in the constructor.

Sub classes

Using sub classes with Jackson is pretty easy. All you need to do is - sit back and relax. The following Object AdvancedContent.java is deserialized as below

public class AdvancedContent extends Content {
   private final String subContent;

   public AdvancedContent(@JsonProperty("header") String title, @JsonProperty("content") String content,
         @JsonProperty("subContent") String subContent, @JsonProperty("comment") String comment) {
      super(title, content, comment);
      this.subContent = subContent;
   }

   public String getSubContent() {
      return subContent;
   }

}

And the JSON created is
{
    "header": "title 4",
    "content": "content 4",
    "subContent": "sub content 4",
    "comment": "comment 4"
}

See, the parent fields are automatically available on serialization. On de-serialization too, the parent fields will be used.

To ignore a property of parent class, use @JsonIgnoreProperties.

Associations

When an object is build of multiple other objects, that is an association. And it is simple to work with associations. Even String objects in POJO are associated to create Content object. And they are serialized and de-serialized without any special attention.

Try out yourself. And use @JsonIgnore, @JsonProperty annotations as need be.

Other configurations

Jackson can be configured using the ObjectMapper itself. Below we shall see a few common configurations.

Ignoring null properties

Many a times our objects are not full - some of their properties are null. We do not want null appearing all over the JSON right?

mapper.setSerializationInclusion(Include.NON_NULL); 

Dates

Date objects are often toughest to work with. And they can be serialized in multiple ways. So here is how we can ask Jackson to serialize dates for us.

mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));

Unknown properties

Many a times we get unknown properties in JSON. So either we ignore them and go ahead, or we fail the parsing. Default is to go on. But certain scenarios warrant us to fail. And this is how we fail

mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

There are many other configurations we may need. Here is a documentation of a few of them.

Other tricks

Strings as raw json

There are a few scenarios where our object itself contains a JSON object already (obviously as a String). So when we let Jackson serialize them, it messes with them.
The quotes are messed up. E.g. below

public class Content {
   @JsonProperty("header")
   private final String title;
   private final String jsonContent;

   public Content(@JsonProperty("header") String title, @JsonProperty("jsonContent") String jsonContent) {
      this.title = title;
      this.jsonContent = jsonContent;
   }

   public String getTitle() {
      return title;
   }

   public String getJsonContent() {
      return jsonContent;
   }
}
And the converted JSON would be
{
    "header": "title 4",
    "jsonContent": "{\"prop1\":\"val-prop-1\",\"prop2\":\"val-prop-2\"}"
}

We have an annotation to rescue. We annotate the jsonContent as below

@JsonRawValue
private final String jsonContent;

And the response magically changes to
{
    "header": "title 4",
    "jsonContent": {
        "prop1": "val-prop-1",
        "prop2": "val-prop-2"
    }
}

Conclusion

It is impossible to cover all the possible scenarios/tricks we can do with Jackson, but this was a shot to get us started.
I hope this article helps you in achieving your goal.

Please do reach me at harsh.curse@gmail.com or post a comment if I went wrong somewhere or if there is something I should cover as a part of this article.

Thanks,
Harsh

Comments

  1. Thanks for the useful article.

    ReplyDelete

  2. Hi there everyone, I have come across almost all the web development sites. However, your website is far better than other in form of content, tools and easiness of using website. Since I am a developer, I am always looking forward to make people aware of web development tools. I can help you people out by introducing you to range of json tools. Here is the link jsononline



    ReplyDelete
    Replies
    1. Thank you Perry. I really like https://jsononline.net/. It is neat, and has very good tools that I would love to use for my development.

      Delete
  3. Good web site you have got here.. It’s hard to find quality writing like yours nowadays. Please see does starbucks take ebt

    ReplyDelete
  4. Portable and wireless, Kindle is the most popular online book reading device. Amazon is a famous American corporation that produces it. In 2007, the first Kindle was released. Since its release, the Amazon company's Kindle has made its way into the hearts of multiple users, which has made it the world's most renowned and celebrated e-reading platform. Read more: https://crawlinfo.com/services/how-to-reach-kindle-support-number/

    ReplyDelete

Post a Comment