Jackson JSON Request and Response Mapping in Spring Boot
In this tutorial, we’ll learn how to map Jackson JSON request and response in Spring Boot Application with various Jackson configurations.
Jackson JSON Mapper
When you create a @RestController
in a Spring Boot application to define API endpoints then Jackson JSON ObjectMapper is default HTTP Converter of your REST application which does two things:
- Convert the incoming JSON Request Body to Java Object of your method
@RequestBody
argument. Generally used inPOST
HTTP methods. - Convert the returned Java Object to JSON Response. Generally used in
GET
HTTP methods.
Its good to know that the process of converting:
- Java Object to JSON is known as Marshalling, or Serialization, and
- JSON to Java Object is called Unmarshalling, or Deserialization
Examples
Let’s define a UserController
with GET
and POST
HTTP methods:
package com.example.api.controller;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public Users getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Long createUser(@RequestBody User user) {
return userService.createUser(user);
}
}
Here is our User
Object Model for JSON request and response mapping:
package com.example.api.model;
public class User {
private Long id;
private String name;
private LocalDate dateOfBirth;
private LocalDateTime lastLogin;
/* Getters and Setters */
}
Let’s look at various important configurations and their impact on API request and response
Prevent Failure on Unknown Property in JSON Request Body
If there are unknown properties in JSON Request Body which cannot be mapped to Java Object then Jackson ObjectMapper throw UnrecognizedPropertyException. This feature is enabled by default.
Let’s add extra field gender
in POST request body which is not there in User
request mapping Object. It will throw exception.
spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES = true (default)
application.yml
spring:
jackson:
deserialization:
FAIL_ON_UNKNOWN_PROPERTIES: true
Request
curl -X POST \
http://localhost:8080/users \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"id": 1,
"name": "Ashish",
"dateOfBirth": "1986-08-22",
"gender": "male"
}'
Response Error
JSON parse error: Unrecognized field "gender" (class com.example.api.model.User), not marked as ignorable;
nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "gender" (class com.example.api.model.User), not marked as ignorable (3 known properties: "dateOfBirth", "id", "name"])
We can disable this feature to allow unknown properties (or extra fields) in our JSON Request Body. Please note that if you are using Spring Boot’s default ObjectMapper then you don’t need to do anything as this feature is disabled by default.
We see that POST request did not fail this time and returned id
of created user.
spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES = false
application.yml
spring:
jackson:
deserialization:
FAIL_ON_UNKNOWN_PROPERTIES: false
Request
curl -X POST \
http://localhost:8080/users \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"id": 1,
"name": "Ashish",
"dateOfBirth": "1986-08-22",
"gender": "male"
}'
Response
1
Don’t allow certain property in JSON Request Body
Sometime we don’t want certain properties such as id
to be sent in request body because you would be generating that id
in backend. In such case you can annotate such properties with @JsonIgnore
and enable FAIL_ON_IGNORED_PROPERTIES
feature.
This feature throw IgnoredPropertyException if ignored properties are passed in JSON Request Body. This feature is disabled by default.
We can annotate the id
property in this way:
package com.example.api.model;
public class User {
@JsonIgnore
private Long id;
private String name;
private LocalDate dateOfBirth;
private LocalDateTime lastLogin;
/* Getters and Setters */
}
Let’s see the default behavior first where it allow ignored properties:
spring.jackson.deserialization.FAIL_ON_IGNORED_PROPERTIES = false (default)
application.yml
spring:
jackson:
deserialization:
FAIL_ON_IGNORED_PROPERTIES: false
Request
curl -X POST \
http://localhost:8080/users \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"id": 1,
"name": "Ashish",
"dateOfBirth": "1986-08-22"
}'
Response Error
1
We see that POST request throw exception when we pass id
in JSON Request Body once we enable this feature
spring.jackson.deserialization.FAIL_ON_IGNORED_PROPERTIES = true
application.yml
spring:
jackson:
deserialization:
FAIL_ON_IGNORED_PROPERTIES: true
Request
curl -X POST \
http://localhost:8080/users \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"id": 1,
"name": "Ashish",
"dateOfBirth": "1986-08-22"
}'
Response
JSON parse error: Ignored field "id" (class com.example.api.model.User) encountered;
mapper configured not to allow this;
nested exception is com.fasterxml.jackson.databind.exc.IgnoredPropertyException:
Ignored field "id" (class com.example.api.model.User) encountered;
mapper configured not to allow this (3 known properties: "dateOfBirth", "name", "lastLogin"])
Pretty Print JSON Response
API JSON responses are not pretty print (formatted) by default.
spring.jackson.serialization.INDENT_OUTPUT = false (default)
application.yml
spring:
jackson:
serialization:
INDENT_OUTPUT: false
Request
curl -X GET http://localhost:8080/users
Response
{"users":[{"id":1,"name":"Adam","dateOfBirth":"1950-01-01"},{"id":2,"name":"Bob","dateOfBirth":"1990-10-30"},{"id":3,"name":"Charlie","dateOfBirth":"1979-07-26"}]}
We can pretty print JSON Response by turning on the INDENT_OUTPUT
property . We see that JSON response is formatted after that.
spring.jackson.serialization.INDENT_OUTPUT = true
application.yml
spring:
jackson:
serialization:
INDENT_OUTPUT: true
Request
curl -X GET http://localhost:8080/users
Response
{
"users" : [ {
"id" : 1,
"name" : "Adam",
"dateOfBirth" : "1950-01-01"
}, {
"id" : 2,
"name" : "Bob",
"dateOfBirth" : "1990-10-30"
}, {
"id" : 3,
"name" : "Charlie",
"dateOfBirth" : "1979-07-26"
} ]
}
Format Date and DateTime properties in JSON Response
Date and DateTime fields in Java Object, are converted into numeric timestamp by default during JSON serialization.
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = true (default)
application.yml
spring:
jackson:
serialization:
WRITE_DATES_AS_TIMESTAMPS: true
Request
curl -X GET http://localhost:8080/users/1
Response
{
"id" : 1,
"name" : "Adam",
"dateOfBirth" : [ 1950, 1, 1 ],
"lastLogin" : [ 2020, 7, 3, 0, 26, 22, 211000000 ]
}
We can disable this feature to allow Jackson to convert Date and DateTime fields to human readable String format. Please note that if you are using Spring Boot’s default ObjectMapper then this feature is disabled by default.
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false
application.yml
spring:
jackson:
serialization:
WRITE_DATES_AS_TIMESTAMPS: false
Request
curl -X GET http://localhost:8080/users/1
Response
{
"id" : 1,
"name" : "Adam",
"dateOfBirth" : "1950-01-01",
"lastLogin" : "2020-07-03T00:28:32.394"
}
We see that date and time are in human readable format in JSON response after disabling this feature. We can further customize the Date and DateTime field by annotating them with @JsonFormat
annotations in our User
Object Model.
package com.example.api.model;
public class User {
private Long id;
private String name;
@JsonFormat(pattern="dd MMM yyyy")
private LocalDate dateOfBirth;
@JsonFormat(pattern="dd MMM yyyy hh:mm:ss")
private LocalDateTime lastLogin;
@JsonFormat(pattern = "yyyy-MM-dd@HH:mm:ss.SSSXXX", locale = "en_SG", timezone = "Asia/Singapore")
private ZonedDateTime zonedDateTime;
/* Getters and Setters */
}
Output will be something like this after applying @JsonFormat
annotations:
application.yml
spring:
jackson:
serialization:
WRITE_DATES_AS_TIMESTAMPS: false
Request
curl -X GET http://localhost:8080/users/1
Response
{
"id" : 1,
"name" : "Adam",
"dateOfBirth" : "01 Jan 1950",
"lastLogin" : "03 Jul 2020 01:03:34",
"zonedDateTime" : "2020-07-03@01:03:34.467+08:00"
}
Please note that once you apply @JsonFormat
annotation on Date and DateTime fields, same format would be used for JSON deserialization. That means you need to pass date or datetime parameters in JSON request body of an HTTP request in same format.
Conclusion
We looked at some of the useful configurations. Here is the full list of Jackson serialization and deserialization properties configurable in Spring Boot Application using application.yml, or application.properties file.
application.yml
spring:
jackson:
serialization:
CLOSE_CLOSEABLE: true/false
EAGER_SERIALIZER_FETCH: true/false
FAIL_ON_EMPTY_BEANS: true/false
FAIL_ON_SELF_REFERENCES: true/false
FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS: true/false
FLUSH_AFTER_WRITE_VALUE: true/false
INDENT_OUTPUT: true/false
ORDER_MAP_ENTRIES_BY_KEYS: true/false
USE_EQUALITY_FOR_OBJECT_ID: true/false
WRAP_EXCEPTIONS: true/false
WRAP_ROOT_VALUE: true/false
WRITE_BIGDECIMAL_AS_PLAIN: true/false
WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS: true/false
WRITE_DATES_AS_TIMESTAMPS: true/false
WRITE_DATES_WITH_ZONE_ID: true/false
WRITE_DATE_KEYS_AS_TIMESTAMPS: true/false
WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS: true/false
WRITE_DURATIONS_AS_TIMESTAMPS: true/false
WRITE_EMPTY_JSON_ARRAYS: true/false
WRITE_ENUMS_USING_INDEX: true/false
WRITE_ENUMS_USING_TO_STRING: true/false
WRITE_ENUM_KEYS_USING_INDEX: true/false
WRITE_NULL_MAP_VALUES: true/false
WRITE_SELF_REFERENCES_AS_NULL: true/false
WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED: true/false
deserialization:
ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT: true/false
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT: true/false
ACCEPT_FLOAT_AS_INT: true/false
ACCEPT_SINGLE_VALUE_AS_ARRAY: true/false
ADJUST_DATES_TO_CONTEXT_TIME_ZONE: true/false
EAGER_DESERIALIZER_FETCH: true/false
FAIL_ON_IGNORED_PROPERTIES: true/false
FAIL_ON_INVALID_SUBTYPE: true/false
FAIL_ON_MISSING_CREATOR_PROPERTIES: true/false
FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY: true/false
FAIL_ON_NULL_CREATOR_PROPERTIES: true/false
FAIL_ON_NULL_FOR_PRIMITIVES: true/false
FAIL_ON_NUMBERS_FOR_ENUMS: true/false
FAIL_ON_READING_DUP_TREE_KEY: true/false
FAIL_ON_TRAILING_TOKENS: true/false
FAIL_ON_UNKNOWN_PROPERTIES: true/false
FAIL_ON_UNRESOLVED_OBJECT_IDS: true/false
READ_DATE_TIMESTAMPS_AS_NANOSECONDS: true/false
READ_ENUMS_USING_TO_STRING: true/false
READ_UNKNOWN_ENUM_VALUES_AS_NULL:true/false
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE: true/false
UNWRAP_ROOT_VALUE: true/false
UNWRAP_SINGLE_VALUE_ARRAYS: true/false
USE_BIG_DECIMAL_FOR_FLOATS: true/false
USE_BIG_INTEGER_FOR_INTS: true/false
USE_JAVA_ARRAY_FOR_JSON_ARRAY: true/false
USE_LONG_FOR_INTS: true/false
WRAP_EXCEPTIONS: true/false
Please note that spring boot configuration support Relaxed Binding that means properties can be in uppercase or lowercase, both are valid.
spring:
jackson:
serialization:
INDENT_OUTPUT: true
is same as
spring:
jackson:
serialization:
indent_output: true
That’s it for now. I’ll keep updating this post with more practical use cases as I come across.
Download the source code for these examples from github/springboot-api