Core Java

Jackson and Lombok Examples

1. Introduction

Jackson is an open-source java library for processing JSONs. It deserializes a JSON string into a Plain Old Java Object (POJO) and serializes a POJO into JSON string. Each field in the JSON object corresponds to a field in the Java POJO class. Lombok is an open-source project that reduces boilerplate code by generating common methods like getters, setters, constructors, toString, equals, and hashCode at compile time. In this example, I will create a maven project which includes several Jackson and Lombok examples. The POJO will be annotated with both Jackson and Lombok annotations.

2. Setup Lombok

The Lombok library requires additional installation steps. Please follow the instructions outlined at https://projectlombok.org/setup/.

For this example, I installed the Lombok plugin at Eclipse IDE with the following steps:

  • download the lombok.jar from https://projectlombok.org/download.
  • launch the lombok installer via the java command. Here is mine: C:\MaryTools\jdk-17\bin>java -jar c:\Users\azpm0\Downloads\lombok.jar.
  • click the “Install/Update” button to complete the installation after the Lombok installer is launched.
  • restart the Eclipse IDE and verify that the Lombok is installed by viewing the “About Eclipse IDE” page as it should include “Lombok {version} is installed”. Note, the {version} may be different from the screenshot here.

3. Maven Project

In this example, I will create a pom.xml which includes Jackson, Lombok, and Junit dependencies.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.zheng.demo</groupId>
	<artifactId>jackson-lombok</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>


		<!--
		https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.17.1</version>
		</dependency>


		<!--
		https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.17.1</version>
		</dependency>

		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<version>5.10.2</version>
			<scope>test</scope>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.32</version>
			<scope>provided</scope>
		</dependency>

	</dependencies>
</project>

4. POJOs

In this step, I will create three POJOs and will be used at step 5.

4.1 Order Object

In this example, I will create an Order.java which includes two data members.

  • productName – the optional product name.
  • quantity – the optional quantity value.

Order.java

package org.zheng.demo.data;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class Order {

	private String productName;
	private int quantity;

}
  • Line 10: the Lombok @Data annotation combines @Getter, @Setter, @ToString, @EqualsAndHashCode, and @NoArgsConstructor together.
  • Line 11, 12: The Lombok @AllArgsConstructor and @NoArgsConstructor. Please note that @NoArgsConstructor is needed for Jackson POJO objects.
  • Line 13: the Lombok @Builder generates builder creation.
  • Line 14: the Jackson @JsonIgnoreProperties(ignoreUnknown = true) ignores unknown properties.

You can confirm that the getters, setters, default contructors, equals, hashCode, and toString methods are generated by viewing the Order's outline as the following screenshot.

Figure 2. Order with Generated Code

4.2 Customer Object

In this example, I will create a Customer.java which has five private data members and one method.

  • custTag – an optional customer’s tag.
  • email – an optional customer’s email address.
  • id – a unique identifier.
  • name– a mandatory name field.
  • orders – an optional list of orders belong to this customer.
  • addOrder() – this method adds an order to its orders list. Note: it uses the log generated by the @Log annotation.

Customer.java

package org.zheng.demo.data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;

@Data
@NoArgsConstructor
@RequiredArgsConstructor
@Log
@JsonIgnoreProperties(ignoreUnknown = true)
public class Customer implements Serializable {

	private static final long serialVersionUID = 5963349342478710542L;

	private String custTag;
	private String email;
	private int id;

	@NonNull
	private String name;
	private List<Order> orders;

	public void addOrder(Order order) {
		log.info("addOrder starts for "+ order.toString());
		if (this.orders == null) {
			this.orders = new ArrayList<>();
		}
		this.orders.add(order);
	}

}
  • Line 15: the Lombok @Data annotation combines @Getter, @Setter, @ToString, @EqualsAndHashCode, and @NoArgsConstructor together.
  • Line 16: the Lombok @NoArgsConstructor generates a no-arguments constructor.
  • Line 17: the Lombok @RequiredArgsConstructor generates a constructor for required fields (final fields and fields marked with @NonNull).
  • Line 18: the Lombok @Log generates a java.util.logging.Logger field with field name as log.
  • Line 19: the Jackson @JsonIgnoreProperties(ignoreUnknown = true) ignores unknown properties.
  • Line 28: the Lombok @Nonnull marks the field as non-null value.
  • Line 33: log the message via the log field generated from the Lombok @Log.

4.3 Phone Object

In this example, I will create a Phone.java which contains three data members.

  • countryCode – an optional country code.
  • number – an optional phone number.
  • notInJson – an optional phone data which annotates with @JsonIgnore to ignore during the JSON processing.

Phone.java

package org.zheng.demo.data;

import com.fasterxml.jackson.annotation.JsonIgnore;

import lombok.Data;

@Data
public class Phone {

	private String countryCode;
	@JsonIgnore
	private String notInJson;

	private String number;

}
  • Line 7: the Lombok @Data annotation combines @Getter, @Setter, @ToString, @EqualsAndHashCode, and @NoArgsConstructor together.
  • Line 11: @JsonIgnore ignores the notInJson field.

5. Demo with Junit Tests

In this example, I will create a JacksonLombokTest.java which has two tests.

  • test_customer – first it creates a customer object via the constructor and setters generated from Lombok @Data, @RequiredArgsConstructor, @NoArgsConstructor, @AllArgsConstructor, and @Builder. Then it serializes the customer object into a JSON string, Lastly, it deserializes the JSON string back to the customer object and verifies it is the same as the original customer object via the equals method generated from Lombok.
  • test_phone – first it creates a phone object via the constructor and setter generated from lombok @Data. Then it serializes the phone object into a JSON string, Lastly, it deserializes the JSON string back to the phone object and verifies it is NOT same as the original phone object via the equals method generated from Lombok because the field notInJson is marked with @JsonIgnore.

JacksonLombokTest.java

package org.zheng.demo;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import org.zheng.demo.data.Customer;
import org.zheng.demo.data.Order;
import org.zheng.demo.data.Phone;

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

class JacksonLombokTest {

	private ObjectMapper ob = new ObjectMapper();

	@Test
	void test_customer() {
		Customer customer = new Customer("Zheng");
		customer.setCustTag("major");
		customer.setId(2);
		customer.setEmail("test@test.com");
		Order order = new Order("test", 10);
		customer.addOrder(order);
		Order order2 = Order.builder().productName("PS").quantity(20).build();
		customer.addOrder(order2);

		try {
			String jsonStr = ob.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
			System.out.println(jsonStr);

			Customer readCust = ob.readValue(jsonStr, Customer.class);

			assertTrue(customer.equals(readCust));
			assertEquals("major", readCust.getCustTag());
			assertEquals("test@test.com", readCust.getEmail());
			assertEquals("Zheng", readCust.getName());
			assertEquals(2, readCust.getId());
			assertEquals(2, readCust.getOrders().size());
			assertEquals("test", readCust.getOrders().get(0).getProductName());
			assertEquals(10, readCust.getOrders().get(0).getQuantity());
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
	}

	@Test
	void test_phone() {
		Phone phone = new Phone();
		phone.setCountryCode("001");
		phone.setNotInJson("notInJson");
		phone.setNumber("314-123-4567");

		String jsonStr;
		try {
			jsonStr = ob.writeValueAsString(phone);
			Phone readPhone = ob.readValue(jsonStr, Phone.class);
			assertFalse(readPhone.equals(phone));
			assertNull(readPhone.getNotInJson());
			assertNotNull(phone.getNotInJson());

		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

	}

}
  • Line 23: the customer(name) constructor is generated from @RequiredArgsConstructor.
  • Line 24 – 25: the customer’s setters are generated from @Data.
  • Line 27: the order(productName, quantity) constructor is generated from AllArgsConstructor.
  • Line 29 create an order object via the generated static builder method.
  • Line 33: serialize the customer object into a JSON string.
  • Line 36: deserialize the JSON string into a new customer object.
  • Line 38 – 44: verify the data via the generated getters from Lombok.
  • Line 53-56: create a phone object via the generated constructor and setters.
  • Line 60, 61: serialize the phone object into JSON and deserialize the JSON into a new phone object.
  • Line 62, 63: verify the data via the generated getters from Lombok. Please note, the generated phone is different from the original phone as it has an ignored field.

Execute the Junit test and capture the output.

JacksonLombokTest output

May 30, 2024 7:00:22 AM org.zheng.demo.data.Customer addOrder
INFO: addOrder starts for Order(productName=test, quantity=10)
May 30, 2024 7:00:22 AM org.zheng.demo.data.Customer addOrder
INFO: addOrder starts for Order(productName=PS, quantity=20)
{
  "custTag" : "major",
  "email" : "test@test.com",
  "id" : 2,
  "name" : "Zheng",
  "orders" : [ {
    "productName" : "test",
    "quantity" : 10
  }, {
    "productName" : "PS",
    "quantity" : 20
  } ]
}
  • Line 1-4: the log statements added by the generated log field when the addOrder method is called.

6. Conclusion

As you see in the Jackson and Lombok examples, the Jackson and Lombok annotations help writing cleaner and more maintainable code. Jackson provides annotations to map the POJO fields to JSON and Lombok provides annotations to reduce boilerplate code.

7. Download

This was a maven project which includes both Jackson and Lombok examples to process JSON string while reducing boilerplate code.

Download
You can download the full source code of this example here: Jackson and Lombok Examples

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button