Jackson is a popular library for serializing/deserializing Java objects into various text formats. The ObjectMapper class is the library's main way to work with the JSON format. For other formats, we have its descendants (XmlMapper, YAMLMapper). Thanks to inheritance, we can work with all formats in a consistent way, through a single interface.

Download jar files

Before studying the examples, we need to download Jackson jar files and connect them to the project in IntelliJ IDEA. Let's take the example of jackson-databind to see how to search for needed files:

  1. Go to the Maven Repository website.

  2. Enter "jackson-databind" into the search box. You'll get the following:

  3. The first search result is what we are interested in. Follow the link.

  4. Sometimes a particular version of a library may be required to ensure compatibility with other components in a project. The latest version should work (at the time of writing this lesson, it is 2.13.2.2). Follow the link.

  5. On the page that opens, you want the "bundle" link:

  6. Download the jar file using this link.

Following a similar procedure, you can find and download the rest of the necessary jar files:

After downloading all the necessary files, connect them to the project in IntelliJ IDEA:

  1. Open the project settings (you can do with the Ctrl+Alt+Shift+S key combination).

  2. Go to Libraries.

  3. Press + and then "Java". Select all the downloaded files. Here's what we should end up with:

  4. That concludes our preparatory work. Now we can try out ObjectMapper in action.

Serialization to JSON

First, let's serialize some object to JSON:


import com.fasterxml.jackson.databind.ObjectMapper;
 
class Book {
	public String title;
	public String author;
	public int pages;
}
 
public class Solution {
	public static void main(String[] args) throws Exception {
    	Book book = new Book();
    	book.title = "Good Omens";
    	book.author = "Pratchett T., Gaiman N.";
    	book.pages = 383;
 
    	ObjectMapper mapper = new ObjectMapper();
    	String jsonBook = mapper.writeValueAsString(book);
    	System.out.println(jsonBook);
	}
}

Running main will give you this output:

{"title":"Good Omens","author":"Pratchett T., Gaiman N.","pages":383}

The ObjectMapper has a lot of advanced settings. Let's use one of them to make the JSON string more readable. After creating the ObjectMapper object, execute this statement:

mapper.enable(SerializationFeature.INDENT_OUTPUT);

The information in the output remains the same, but now there is indentation and line breaks:

{
  "title" : "Good Omens",
  "author" : "Pratchett T., Gaiman N.",
 "pages" : 383
}

Deserialization from JSON

Now let's do the opposite action: we'll deserialize a string into an object. To be able to see what the program is doing, let's override the toString method in the Book class:


@Override
public String toString() {
	return "Book{" +
        	"title='" + title + '\'' +
        	", author='" + author + '\'' +
        	", pages=" + pages +
        	'}';
}

And we'll do the following in the main method:


public static void main(String[] args) throws Exception {
	String jsonString = "{\"title\":\"Good Omens\",\"author\":\"Pratchett T., Gaiman N.\",\"pages\":383}";
	Book book = new ObjectMapper().readValue(jsonString, Book.class);
	System.out.println(book);
}

Output:

Book{title='Good Omens', author='Pratchett T., Gaiman N.', pages=383}

The readValue method is overloaded — it has many variations that take a file, a link, various input streams, etc. For simplicity, our example uses a variant that accepts a JSON string.

As mentioned above, ObjectMapper has many settings. Let's look at a few of them.

Ignoring unknown properties

Consider a situation where a JSON string has a property that does not exist in the Book class:


public static void main(String[] args) throws Exception {
	String jsonString = """
        	{
          	"title" : "Good Omens",
          	"author" : "Pratchett T., Gaiman N.",
          	"pages" : 383,
          	"unknown property" : 42
        	}""";
	ObjectMapper mapper = new ObjectMapper();
	Book book = mapper.readValue(jsonString, Book.class);
	System.out.println(book);
}

Executing this code gives us an UnrecognizedPropertyException. This is the default behavior, but we can change it:


ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

When creating an ObjectMapper object, we use the configure method to set the corresponding setting to false. The configure method modifies the object it was called on and then returns that same object, so we can make this call in a different way:


ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

In terms of functionality, this notation is just like the previous one.

If we now run the main method, the deserialization will succeed and the unknown property will be ignored.

Convenient annotations

Jackson provides us with several annotations that allow us to customize the serialization process in all sorts of ways. Let's take a look at some of the most useful ones:

@JsonIgnore — This annotation is placed above an element that should be ignored during serialization/deserialization:


class Book {
	public String title;
	@JsonIgnore
	public String author;
	public int pages;
}

Here the author field will not be included in the resulting JSON during serialization. Upon deserialization, the author field will get the default value (null), even if the JSON had a different value.

@JsonFormat — This annotation lets you set the format of the serialized data. Let's add one more field, a Date, to the Book class:


class Book {
	public String title;
	public String author;
	public int pages;
	public Date createdDate = new Date();
}

After serialization, we get the following JSON:

 {
  "title" : "Good Omens",
  "author" : "Pratchett T., Gaiman N.",
  "pages" : 383,
  "createdDate" : 1649330880788
}

As you can see, the date was serialized as a number. We'll add an annotation and set the format:


class Book {
	public String title;
	public String author;
	public int pages;
	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
	public Date createdDate = new Date();
}

Now the result of serialization is:

{
  "title" : "Good Omens",
  "author" : "Pratchett T., Gaiman N.",
  "pages" : 383,
  "createdDate" : "2022-04-07"
}

@JsonProperty — This annotation lets you change the name of the property representing the serialized field. You can also mark methods with this annotation. If you do, then their return value will be converted into a JSON property during serialization:


class Book {
	@JsonProperty("name")
	public String title;
	public String author;
	public int pages;
 
	@JsonProperty("quotedTitle")
	public String getQuotedTitle() {
    	    return "\"" + title + "\"";
	}
}

Result of seralization:

{
  "author" : "Pratchett T., Gaiman N.",
  "pages" : 383,
  "name" : "Good Omens",
  "quotedTitle" : "\"Good Omens\""
}

@JsonInclude — Using this annotation, you can specify conditions that must be satisfied for a field to be serialized. You can apply it to individual fields or an entire class. First, let's try to serialize an object with uninitialized fields:


public class Solution {
	public static void main(String[] args) throws Exception {
    		Book book = new Book();

    		ObjectMapper mapper = new ObjectMapper();
    		mapper.enable(SerializationFeature.INDENT_OUTPUT);
    		String jsonBook = mapper.writeValueAsString(book);
    		System.out.println(jsonBook);
	}
}

Result of seralization:

{
  "title" : null,
  "author" : null,
  "pages" : 0
}

And if you add the annotation:


@JsonInclude(JsonInclude.Include.NON_NULL)
class Book {
	public String title;
	public String author;
	public int pages;
}

Then we get this result:

{
  "pages" : 0
}

Now fields that are null are not serialized.

@JsonPropertyOrder — This annotation lets you set the order in which fields are serialized:


@JsonPropertyOrder({"author", "title", "pages"})
class Book {
	public String title;
	public String author;
	public int pages;
}

Result of seralization:

{
  "author" : "Pratchett T., Gaiman N.",
  "title" : "Good Omens",
  "pages" : 383
}

For now, just remember how to use annotations. At the end of this module, we'll get to know them better and even create our own annotations.

Serialization and deserialization in XML

If we need to serialize into XML, we can use all the same settings and annotations. The only difference will be the implementation of the mapper object:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new XmlMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String xmlBook = mapper.writeValueAsString(book);
	System.out.println(xmlBook);
}

Output:

 <Book>
  <title>Good Omens</title>
  <author>Pratchett T., Gaiman N.</author>
  <pages>383</pages>
</Book>

Deserialization of XML:


public static void main(String[] args) throws Exception {
   String xmlString = """
            <Book>
             <title>Good Omens</title>
             <author>Pratchett T., Gaiman N.</author>
             <pages>383</pages>
           </Book>""";
   ObjectMapper mapper = new XmlMapper();
   Book book = mapper.readValue(xmlString, Book.class);
   System.out.println(book);
}

Serialization and deserialization in YAML

We handle YAML the same way we handle XML:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new YAMLMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String yamlBook = mapper.writeValueAsString(book);
	System.out.println(yamlBook);
}

Output:

---
title: "Good Omens"
author: "Pratchett T., Gaiman N."
pages: 383

Deserialization of YAML:


public static void main(String[] args) throws Exception {
   String yamlString = """
           ---
           title: "Good Omens"
           author: "Pratchett T., Gaiman N."
           pages: 383""";
   ObjectMapper mapper = new YAMLMapper();
   Book book = mapper.readValue(yamlString, Book.class);
   System.out.println(book);
}