JSON is an alternative to XML for interchanging messages between servers, communicating with RESTful web services, and more. It is a lightweight, language-independent, and human-readable data interchange format that is easy to read and write.
Jackson is a very popular choice for processing JSON data in Java. it consists of three libraries: Jackson Databind (jackson-databind
), Core (jackson-core
), and Annotations (jackson-annotations
).
Since the databind library depends on core and annotations libraries, adding jackson-databind
to the Gradle project dependencies section will also add the other two dependencies.
build.gradle
compile ('com.fasterxml.jackson.core:jackson-databind:2.9.8')
Or if you are using Maven, add the following dependency to your POM file:
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
Since Spring Framework relies on the Jackson parser to convert JSON to object or vice versa, the library is already included in the Spring Boot project dependencies list. You do not need to explicitly include it. Otherwise, you may get version conflict errors.
Note: This tutorial is intended for processing JSON data stored in a file. If you are calling a RESTful web service for JSON data, check out Making HTTP Requests using RestTemplate guide.
JSON File to Object - Jackson Data Binding
Jackson has an ObjectMapper
class for mapping JSON data to Java objects (POJOs). It also supports JSON data conversion to Map and other generic data types.
To thoroughly understand how the mapping works, let us create the following JSON file that includes customer data:
customer.json
{
"id": 123,
"name": "Jovan Lee",
"email": "jovan@example.com",
"phone": "+49 176 14890478",
"age": 32,
"projects": [
"Remote Job Board",
"Data Migration"
],
"address": {
"street": "Yorckstr. 75",
"city": "Berlin",
"zipcode": 10965,
"country": "Germany"
},
"paymentMethods": [
"PayPal",
"Stripe"
],
"profileInfo": {
"gender": "Male",
"married": "Yes",
"source": "LinkedIn"
}
}
The above JSON file contains several JSON objects with key-value pairs, including projects
and paymentMethods
arrays. When you look at the JSON data, it is cleared that we need to create two Java classes for mapping to work: Address
& Customer
.
The Customer
class will contain the Address
class representing the inner address
object and other properties.
Address.java
package com.attacomsian.jackson;
public class Address {
private String street;
private String city;
private int zipcode;
private String country;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getZipcode() {
return zipcode;
}
public void setZipcode(int zipcode) {
this.zipcode = zipcode;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public String toString() {
return "{" +
"street='" + street + '\'' +
", city='" + city + '\'' +
", zipcode=" + zipcode +
", country='" + country + '\'' +
'}';
}
}
Customer.java
package com.attacomsian.jackson;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class Customer {
private Long id;
private String name;
private String email;
private String phone;
private int age;
private String[] projects;
private Address address;
private List<String> paymentMethods;
private Map<String, Object> profileInfo;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String[] getProjects() {
return projects;
}
public void setProjects(String[] projects) {
this.projects = projects;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public List<String> getPaymentMethods() {
return paymentMethods;
}
public void setPaymentMethods(List<String> paymentMethods) {
this.paymentMethods = paymentMethods;
}
public Map<String, Object> getProfileInfo() {
return profileInfo;
}
public void setProfileInfo(Map<String, Object> profileInfo) {
this.profileInfo = profileInfo;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
", age=" + age +
", projects=" + Arrays.toString(projects) +
", address=" + address +
", paymentMethods=" + paymentMethods +
", profileInfo=" + profileInfo +
'}';
}
}
Customer
POJO represents the root JSON object. After creating POJO classes, now let's perform the data binding using Jackson's ObjectMapper
classes.
JacksonApplication.java
package com.attacomsian.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.io.File;
import java.io.IOException;
@SpringBootApplication
public class JacksonApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(JacksonApplication.class, args);
}
@Override
public void run(String[] args) throws IOException {
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//read JSON file and convert to a customer object
Customer customer = objectMapper.readValue(new File("customer.json"), Customer.class);
//print customer details
System.out.println(customer);
}
}
In the above JacksonApplication
class, we created an ObjectMapper
instance and called its readValue()
method with two parameters: a File
object representing the JSON file as a source and Customer.class
as the target to map the JSON values. In return, we get a Customer
object populated with the data read from the customer.json
file.
When we run the Spring Boot project, we will see the following output printed on the console:
Customer{id=123, name='Jovan Lee', email='jovan@example.com', phone='+49 176 14890478', age=32, projects=[Remote Job Board, Data Migration], address={street='Yorckstr. 75', city='Berlin', zipcode=10965, country='Germany'}, paymentMethods=[PayPal, Stripe], profileInfo={gender=Male, married=Yes, source=LinkedIn}}
Java Object to JSON File
The ObjectMapper
class also provides methods to transform a Java object into a JSON file or string. For example, the writeValue()
method converts byte arrays, files, and output streams into strings.
Let us update our JacksonApplication
class with an example of Customer
object conversion to the JSON file.
JacksonApplication.java
package com.attacomsian.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootApplication
public class JacksonApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(JacksonApplication.class, args);
}
@Override
public void run(String[] args) throws IOException {
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//read JSON file and convert to a customer object
Customer customer = objectMapper.readValue(new File("customer.json"), Customer.class);
//print customer details
System.out.println(customer);
//create a customer object
Customer customerObj = new Customer();
customerObj.setId(567L);
customerObj.setName("Maria Kovosi");
customerObj.setEmail("maria@example.com");
customerObj.setPhone("+12 785 4895 321");
customerObj.setAge(29);
customerObj.setProjects(new String[]{"Path Finder App", "Push Notifications"});
Address address = new Address();
address.setStreet("Karchstr.");
address.setCity("Hanover");
address.setZipcode(66525);
address.setCountry("Germany");
customerObj.setAddress(address);
List<String> paymentMethods = new ArrayList<>();
paymentMethods.add("PayPal");
paymentMethods.add("SOFORT");
customerObj.setPaymentMethods(paymentMethods);
Map<String, Object> info = new HashMap<>();
info.put("gender", "female");
info.put("married", "No");
info.put("income", "120,000 EURO");
info.put("source", "Google Search");
customerObj.setProfileInfo(info);
//configure objectMapper for pretty input
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
//write customerObj object to customer2.json file
objectMapper.writeValue(new File("customer2.json"), customerObj);
}
}
Our updated JacksonApplication
class now maps the JSON file to the Customer
object and then creates a new Customer
object and stores it in the customer2.json
file.
customer2.json
{
"id" : 567,
"name" : "Maria Kovosi",
"email" : "maria@example.com",
"phone" : "+12 785 4895 321",
"age" : 29,
"projects" : [ "Path Finder App", "Push Notifications" ],
"address" : {
"street" : "Karchstr.",
"city" : "Hanover",
"zipcode" : 66525,
"country" : "Germany"
},
"paymentMethods" : [ "PayPal", "SOFORT" ],
"profileInfo" : {
"income" : "120,000 EURO",
"gender" : "female",
"source" : "Google Search",
"married" : "No"
}
}
JSON File to Map - Simple Data Binding
Sometimes we do not need full data binding - JSON data conversion to Java objects with the same properties and keys. Instead, we simply want to convert our JSON data into Map or List or any other built-in Java types, for example, Boolean, String, or even Double. The following is an example of simple data binding of our customer.json
file to a generic Map
:
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//convert JSON file to map
Map<?, ?> map = objectMapper.readValue(new FileInputStream("customer.json"), Map.class);
//iterate over map entries and print to console
for (Map.Entry<?, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
Notice the overloaded readValue()
method that accepts a FileInputStream
object to read the customer.json
file. This method has even more variants to support String
, Reader
, URL
, and byte array. When we execute the above code snippet, we get the following output:
id=123
name=Jovan Lee
email=jovan@example.com
phone=+49 176 14890478
age=32
projects=[Remote Job Board, Data Migration]
address={street=Yorckstr. 75, city=Berlin, zipcode=10965, country=Germany}
paymentMethods=[PayPal, Stripe]
profileInfo={gender=Male, married=Yes, source=LinkedIn}
JSON File into a Tree Model
The ObjectMapper
class can also be used to construct a hierarchical tree of nodes from JSON data. In the JSON Tree Model, you can access a specific node and read its value.
In the Tree Model, each node is of type JsonNode
that provides different methods to work with specific keys.
This method is useful when you are interested in only a few key values. In this scenario, it does not make sense to convert the complete JSON file into a Java object. The following code snippet converts the customer.json
file into a Tree Model and then reads the name
and phone
nodes values.
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//read customer.json file into a tree model
JsonNode rootNode = objectMapper.readTree(new File("customer.json"));
//read name and phone nodes
System.out.println("Customer Name: " + rootNode.path("name").asText());
System.out.println("Customer Phone: " + rootNode.path("phone").asText());
System.out.println("Customer Age: " + rootNode.path("age").asInt());
System.out.println("Customer City: " + rootNode.path("address").path("city").asText());
System.out.println("Customer Project: " + rootNode.path("projects").get(0).asText());
In the code above, we used the readTree()
method from the ObjectMapper
class to read our customer.json
file in a JSON Tree Model. The JsonNode
class provides a path()
method that can be used to specify the node name we are interested in to read the value. Since path()
returns another JsonNode
object, it can be chained to get a nested node (like the city value in the above code). The asText()
method returns the value of the node as a string.
Another key thing to note in the above code snippet is the use of the get()
method instead of path()
to read the first project value. It works similar to path()
method - returns the specific node as JsonNode
. However, it returns a null
value when the node is not present or is empty, instead of the missing node object as returned by the path()
method. The get()
method is also used to access the unknown keys and array indexes.
Source code: Download the complete source code from GitHub available under MIT license.
Conclusion
That is all for parsing JSON data using Jackson in a Spring Boot project. It is not the only JSON processor library available for Java. There are few more options available like Google Gson, Boon and core Java API. However, Jackson is very popular due to its ease of use and a lot of options available for processing JSON.
Further Reading
Want to learn more about Spring Boot? Check out the below tutorials:
- Reading and Writing JSON Files in Java
- How to read and write JSON using Jackson in Java
- How to read and write JSON using JSON.simple in Java
- How to Schedule Tasks in Spring Boot
- How to upload a file with Ajax in Spring Boot
✌️ Like this article? Follow me on Twitter and LinkedIn. You can also subscribe to RSS Feed.