Spring Boot JSON
last modified July 28, 2023
Spring Boot JSON tutorial shows how to serve JSON data in a Spring Boot annotation.
Spring is a popular Java application framework and Spring Boot is an evolution of Spring that helps create stand-alone, production-grade Spring based applications easily.
JSON
JSON (JavaScript Object Notation) is a lightweight data-interchange
format. It is easy for humans to read and write and for machines to parse and
generate. The official Internet media type for JSON is
application/json. The JSON filename extension is
.json.
Spring Boot JSON
Spring Boot provides integration with three JSON mapping libraries:
- Gson
- Jackson
- JSON-B
Jackson is the preferred and default library.
spring.http.converters.preferred-json-mapper=jsonb
The preferred JSON converter can be set with the
spring.http.converters.preferred-json-mapper property.
Jackson
Jackson is a suite of data-processing tools for Java. It allows to read and write data in JSON, Avro, BSON, CBOR, CSV, Smile, (Java) Properties, Protobuf, XML or YAML format.
Jackson is auto-configured. It comes with the
spring-boot-starter-json. When Jackson is on the classpath an
ObjectMapper bean is automatically configured. The
spring-boot-starter-json is pulled with the
spring-boot-starter-web.
In Spring objects are automatically convered to JSON with the Jackson library. Spring can be configured to convert to XML as well.
spring.jackson.date-format= # For instance, `yyyy-MM-dd HH:mm:ss`. spring.jackson.default-property-inclusion= # including properties during serialization. spring.jackson.deserialization.*= # Jackson on/off features for deserialization. spring.jackson.generator.*= # Jackson on/off features for generators. spring.jackson.joda-date-time-format= # Joda date time format string. spring.jackson.locale= # Locale used for formatting. spring.jackson.mapper.*= # Jackson general purpose on/off features. spring.jackson.parser.*= # Jackson on/off features for parsers. spring.jackson.property-naming-strategy= # PropertyNamingStrategy. spring.jackson.serialization.*= # Jackson on/off features for serialization. spring.jackson.time-zone= # Time zone spring.jackson.visibility.*= # To limit which methods (and fields) are auto-detected.
Jackson can be configured with application properties.
@Configuration
public class WebConfig
{
@Bean
public ObjectMapper customJson() {
return new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.propertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)
.build();
}
}
Jackson can be configured with the Jackson2ObjectMapperBuilder.
@Bean
public Jackson2ObjectMapperBuilderCustomizer customJson()
{
return builder -> {
builder.indentOutput(true);
builder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
};
}
Existing configuration can be modified with the Jackson2ObjectMapperBuilderCustomizer.
Spring Boot JSON example
The following application return JSON data to the client. The Jackson is configured in three different ways.
build.gradle
...
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───config
│ │ │ WebConfig.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───model
│ │ │ City.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ application.yml
└── test
├── java
└── resources
This is the project structure.
plugins {
id 'org.springframework.boot' version '3.1.1'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
This is the Gradle build.gradle file.
spring:
main:
log-startup-info: false
jackson:
property-naming-strategy: UPPER_CAMEL_CASE
serialization:
indent-output: true
In the application.yml file, we set Jackson properties. These
settings can be overwritten or customized with the configuration beans.
package com.zetcode.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Objects;
public class City {
@JsonIgnore
private Long id;
private String name;
private int population;
public City() {
}
public City(Long id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("City{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append(", population=").append(population);
sb.append('}');
return sb.toString();
}
}
We have a City model class.
@JsonIgnore private Long id;
With the @JsonIgnore, we remove the id from
JSON serialization.
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> getCities();
}
The ICityService contains the getCities contract
method.
package com.zetcode.service;
import com.zetcode.model.City;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CityService implements ICityService {
public List<City> getCities() {
List<City> cities = new ArrayList<>();
cities.add(new City(1L, "Bratislava", 432000));
cities.add(new City(2L, "Budapest", 1759000));
cities.add(new City(3L, "Prague", 1280000));
cities.add(new City(4L, "Warsaw", 1748000));
cities.add(new City(5L, "Los Angeles", 3971000));
cities.add(new City(6L, "New York", 8550000));
cities.add(new City(7L, "Edinburgh", 464000));
cities.add(new City(8L, "Berlin", 3671000));
return cities;
}
}
The CityService returns a list of city objects. We do not use
a database to make the example simpler.
package com.zetcode.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
@Configuration
public class WebConfig {
// @Bean
public ObjectMapper configureJson() {
return new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.propertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE)
.build();
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizeJson() {
return builder -> {
builder.indentOutput(true);
builder.propertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE);
};
}
}
In WebConfig, we have beans that overwrite and customize Jackson
settings. Play with the different settings and see how they are applied.
Enable or disable the beans (by commenting the @Bean) and compare
the outcome.
package com.zetcode.controller;
import com.zetcode.model.City;
import com.zetcode.service.ICityService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class MyController {
private final ICityService cityService;
public MyController(ICityService cityService) {
this.cityService = cityService;
}
@GetMapping("/cities")
public List<City> getCities() {
return cityService.getCities();
}
}
In order to write data to the response body, we use the @RestController
or the @Controller/@ResponseBody combination.
@GetMapping("/cities")
public List<City> getCities() {
return cityService.getCities();
}
The getCities method returns a list of city objects. The
objects are automatically serialized into JSON.
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application is the entry point which sets up Spring Boot
application.
We run the application with ./gradlew bootRun.
$ curl localhost:8080/cities
[ {
"Name" : "Bratislava",
"Population" : 432000
}, {
"Name" : "Budapest",
"Population" : 1759000
}, {
"Name" : "Prague",
"Population" : 1280000
}, {
"Name" : "Warsaw",
"Population" : 1748000
}, {
"Name" : "Los Angeles",
"Population" : 3971000
}, {
"Name" : "New York",
"Population" : 8550000
}, {
"Name" : "Edinburgh",
"Population" : 464000
}, {
"Name" : "Berlin",
"Population" : 3671000
} ]
This is a sample output. The JSON data is indented and the property naming strategy is UPPER_CAMEL_CASE.
In this article we have shown how to serve JSON data to the client in a Spring Boot application.