DEV Community

Cover image for [Java SpringBoot] How to implement a Custom Deserializer for your Requests
Bruno Barbosa
Bruno Barbosa

Posted on

[Java SpringBoot] How to implement a Custom Deserializer for your Requests

Clique aqui para versão em português

Introduction

Some days ago a situation arose to be resolved at work and I decided to share the solution here on dev.to!

I'll try to summarize the problem as much as I can:
We had a Java SpringBoot API that persisted information in a PostgreSQL database.
This API received an object in the request that was made up of around 200 fields, most of them were strings.

Some time after the development was completed, the need arose for all textual information to be persisted in upper case (LIKE THIS, WITH CAPS LOCK).

The team then met to decide which approach we would follow to modify the API in question.
The first proposed alternative was to receive all the fields and manually use the toUpperCase() method of the String class to convert attribute by attribute to upper case.

As the API had a certain complexity, the time estimated by TL for the task was approximately 1 week.

As we work with tight deadlines, I thought of another approach to reduce the time cost of the solution: make a custom deserializer so that all string fields are converted to upper case upon arrival of the request, when mapped to an object at the input of the API endpoint.

This development took about 30 minutes to complete, between research and development, saving the team almost 1 week of work and worked very well.

I will share with you what was developed using a small use case example API. I would like to make it clear that the objective here is not whether this is the best practice (that's up to you to decide with your team according to your needs and availability), I also didn't worry about making the code more foolproof here in the demonstration, so I didn't include, for example, try catch because this is not the focus of this article.

For those who want to see the **repository **of the code used, here is the link:
GitHub Repository

In the repository you will find the main branch with the project's base code before implementation.
You will also find the branch where the custom deserializer was implemented and, also, a branch where I implemented a custom serializer if you want the application to be output, when assembling the JSON that comes out of your application.
(I'll talk about the serializer in the next article!)

Introducing the application:

To give an example, I created a small application that has the POST /students endpoint as its input port, which expects to receive a JSON in the body of the request that will be mapped to an object of the StudentRequest class. Below is the code for the StudentController.java class

@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping("/students")
    ResponseEntity<StudentResponse> createStudent(@RequestBody StudentRequest studentRequest) {
        return ResponseEntity.ok().body(studentService.createStudent(studentRequest));
    }

}
Enter fullscreen mode Exit fullscreen mode

We can see that this class is receiving injection from the service that will handle the business rules.

Our Request class looks like this (StudentRequest.java):

@Data
public class StudentRequest {

    private Long registration;
    private String name;
    private String lastName;

}
Enter fullscreen mode Exit fullscreen mode

The response has the same fields, but with the name StudentResponse.

This service, StudentService.java, will have the business rules to then persist the Student information in the database. I didn't mind implementing persistence in the database because it's not the focus here, but basically we would have a service like this:

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public StudentResponse createStudent(StudentRequest studentRequest) {

        //some business logics
       // persistance ommited

        return studentMapper.requestToResponse(studentRequest);
    }
}
Enter fullscreen mode Exit fullscreen mode

Note that here, to shorten transformations from the request class to the entity or response class, I am using MapStruct (I will write about MapStruct in another article).

When we run the project, we can see the following behavior:

Postman imagem of the request with text field in lower case being returned as they were received, with no modifications

Right, the code written this way (which you can see in the main branch of the repository) ends up persisting the information as it arrives in the request, without any treatment or conversion of the fields into upper case as we want. Let's go, then, to the implementation of our Deserializer, so that the String fields go through toUpperCase() immediately upon entering the request.

Implementing the Deserializer:

This solution is incredibly simple, let's just create a class called ToUpperCaseConverter.java and make it extend Jackson's StdConverter class.
Inside the diamond operator ( <> ) we will place two types of object, one for input (IN) and one for output (OUT), that is, the StdConverter has as its "signature", StdConverter. As we are going to receive a String and return a String but in upper case, we will use StdConverter.

And then we will override the convert(String value) method by placing our toUpperCase() in the implementation of this method. Our class looks like this:

import com.fasterxml.jackson.databind.util.StdConverter;

public class ToUpperCaseConverter extends StdConverter<String, String> {

    @Override
    public String convert(String value) {
        return value.toUpperCase();
    }

}
Enter fullscreen mode Exit fullscreen mode

This class, for organization purposes, I placed inside a package called utils.

Now, how do we mark the fields that need to undergo this conversion? To do this, we just go to our request class and add the @JsonDeserialize annotation and pass to it which class will apply the custom deserialization we created, that is, the ToUpperCaseConverter.java class. Let's add this annotation to all the fields that we need to perform this conversion, thus creating our Request class:

@Data
public class StudentRequest {

    private Long registration;
    @JsonDeserialize(converter = ToUpperCaseConverter.class)
    private String name;
    @JsonDeserialize(converter = ToUpperCaseConverter.class)
    private String lastName;

}
Enter fullscreen mode Exit fullscreen mode

Now, we can run our API and see the result, as shown in the POSTMAN screenshot below:

Image of the Postman application showing a call with string fields in lower case and the response with fields in upper case after conversion

You can make many adaptations to this logic, including creating a custom Deserializer for the entire Request class! Going field by field instructing what manipulation you want to do with the field, but that's for another post.

I hope you were able to contribute and questions and suggestions can be sent here, I would be very grateful if you have improvements to suggest.

Top comments (0)