FasterXML/jackson Tips for JSON in Java

06/03/2020

Hello, in this article I will try to lead you to go beyond default Jackson usage in Spring Boot applications. My main resource will be official documentation as always. Source codes and additional references can be found at the end of the article.

Introduction

All my examples will be in a Spring Boot project that has Web and Lombok dependencies. I will not list all features and explain them one by one like a documentation. Instead, I will ask some questions and answer them. I hope you will find your questions! If not so, you can ask me on Twitter or e-mail.

Q1: How to create a Java POJO for any JSON schema?
Q2: How to use different field names in POJO to map JSON?
Q3: How to deserialize nested JSON objects?
Q4: How to create a custom deserializer?
Q5: How to deserialize enum values?

What is Jackson?

This article is not a zero-to-hero-like article. So, I will not start from basics but introduce Jackson with a few words. Because you may have been using Jackson library without knowing you do so. Jackson is the most widely used JSON processing library for Java. It has 3 core modules (jackson-core, jackson-annotations, jackson-databind), third-party modules for different integrations. You may have been using them because spring-boot-starter-web includes these three modules(and more) and Jackson is registered as default object mapper library. This is why you can produce JSON data by just returning Java object in your controller in your Spring application.

Q1: How to create a Java POJO for any JSON input?

Perhaps, this is the simplest question that comes to mind. Let’s see an example JSON:

{  
  "title": "2019-2020 Algorithms Final Exam",  
  "lecture": "Algorithms",  
  "examDate": "2020-03-04T09:00:00.000Z",  
  "studentNumber": 75,  
  "questions": [  
    {  
      "questionNumber": 1,  
  "question": "What is an asymptotic notation?"  
  },  
  {  
      "questionNumber": 2,  
  "questions": "Find worst case for insertion sort?"  
  },  
  {  
      "questionNumber": 3,  
  "question": "Which algorithm would you choose to sort 1m numbers, why?"  
  }  
  ]  
}

Let’s analyze this JSON. This is an object that has title (string), lecture (string), examDate (date), studentNumber (numeric value, int is the best option for this case.) and questions (array) fields. So, we should map this JSON to a Java object that has the same fields. All fields are OK but questions array has an object type that has questionNumber(numberic value, int) and question (string) fields.

First of all, create a Question object as below:

@Getter  
@Setter  
@ToString  
class Question {  
  
   private Integer questionNumber;  
  
   private String question;  
  
}

Then, create Exam object.

@Getter  
@Setter  
@ToString  
public class Exam {  
  
   private String title;  
  
   private String lecture;  
  
   private LocalDateTime examDate;  
  
   private Integer studentNumber;  
  
   private List<Question> questions;  
  
}

That’s all. Finally, create a controller.

@PostMapping("/question-one")  
ResponseEntity<Void> questionOne(@RequestBody Exam exam) {  
   log.info("Parsed object: {}", exam);  
   return ResponseEntity.ok().build();  
}

Now, send JSON with any method you like.

We mapped JSON array as List in our POJO. This is not a must, we can also use Java Array (Question[]), Collection<>, Set<>, Iterable<> and any other type that implements Iterable<>.

If you don’t need all of the values in JSON you can omit them in your POJO.

Q2: How to use different field names in POJO to map JSON?

In Q1 we named our fields according to given JSON. But this is not a must. We can use different names with many different options. But I will explain only two of them. So let’s create an example JSON:

{  
  "productTitle": "TWSBI ECO Fountain Pen White M Nib",  
  "productPrice": "30.00",  
  "productCategoryId": 13,  
  "productTagId": 235  
}

As you see, there is a JSON object -let’s call it product- and it has 4 fields. Creating a POJO with the same field names are possible. But removing product... prefix is better.

Option 1:

Create a POJO as below:

@Getter  
@Setter  
@ToString  
public class Q2ProductOptionOne {  
  
    @JsonAlias("productTitle")  
    private String title;  
  
    @JsonAlias("productPrice")  
    private String price;  
  
    @JsonAlias("productCategoryId")  
    private Integer categoryId;  
  
    @JsonAlias("productTagId")  
    private Integer tagId;  
}

@JsonAlias annotation makes possible to define multiple options for a property. So @JsonAlias("productTitle") annotation catches productTitle key in JSON. But don’t forget, this annotation creates alternatives only. Actual property name doesn’t change.

This option works perfectly but not the best option. Because what we want is to change the property name not to create alternatives.

Option 2:

@JsonProperty annotation is the other option. This will change the property name:

@Getter  
@Setter  
@ToString  
public class Q2ProductOptionTwo {  
  
    @JsonProperty("productTitle")  
    private String title;  
  
    @JsonProperty("productPrice")  
    private String price;  
  
    @JsonProperty("productCategoryId")  
    private Integer categoryId;  
  
    @JsonProperty("productTagId")  
    private Integer tagId;  
}

Also: We can use these two annotations at the same time. @JsonProperty will change the actual name and @JsonAlias will add other options.

Also: You can set an access option on @JsonProperty. This will provide you some options such as changing the property name on deserialization/serialization/both.

Github Repo: https://github.com/kamer/thymeleaf-crash-course

For questions, suggestions or corrections feel free to reach me on:

Email: kamer@kamerelciyar.com

Twitter: https://twitter.com/kamer_ee