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.
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 thelog
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@Gette
r, @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 aslog
. - 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 thenotInJson
field.
5. Demo with Junit Tests
In this example, I will create a JacksonLombokTest.java
which has two tests.
test_customer
– first it creates acustomer
object via the constructor and setters generated from Lombok@Data
,@RequiredArgsConstructor
,@NoArgsConstructor
,@AllArgsConstructor
, and@Builder
. Then it serializes thecustomer
object into a JSON string, Lastly, it deserializes the JSON string back to the customer object and verifies it is the same as the originalcustomer
object via theequals
method generated from Lombok.test_phone
– first it creates aphone
object via the constructor and setter generated from lombok@Data
. Then it serializes thephone
object into a JSON string, Lastly, it deserializes the JSON string back to the phone object and verifies it is NOT same as the originalphone
object via theequals
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 fromAllArgsConstructor
. - 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 theaddOrder
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.
You can download the full source code of this example here: Jackson and Lombok Examples