How to dynamically ignore fields while serializing java objects to json use jackson and spring boot

When we developed the Restful API, the returned data was usually in JSON format, to ignore fields while serializing POJO objects to json, the Jackson library provides two annotations: @JsonIgnore and @JsonIgnoreProperties to ignore fields. But both these annotations need the fields at compile time itself. We want the fields to be ignored have to be decided at the runtime, based on the endpoint called.
The following tutorial will solve this problem:

Technology

– Java 11
– Spring Boot 2.4.5 (with Spring Web MVC, Spring Data JPA)
– H2 Database
– Lombok
– Maven 3.6.1

Project Structure

domain:  The entity class in this package corresponds to table in database.
dto: Data transfer objects corresponds to entity class.
repository: expose interface of data access layer
service: expose interface of business services
web: expose endpoint rest API.
InitData.java: Initialize data in database
application.properties: Configuration for Spring Datasource, JPA & Hibernate.

-> Here, we can use MappingJacksonValue to ignore fields dynamically.

Add the JsonFilter annotation on the POJO class:


@JsonFilter("authorFilter")
public class AuthorDto {

    private Long id;
    private String name;
    private String email;
    private String gender;

    // constructors, getters & setters are ignored.
}

@JsonFilter("bookFilter")
public class BookDto {

    private Long id;
    private String name;
    private double price;
    private int quantity;
    private Set authors = new HashSet<>();

    // constructors, getters & setters are ignored.
}
  
In the rest controller class BookRestApi.java:
  • Pass the name of the fields to be ignored to the serializeAllExcept method from the SimpleBeanPropertyFilter class.
  • Pass the JsonFilter name and SimpleBeanPropertyFilter object to the addFilter method.
  • Pass the instance of the POJO and filter class to the MappingJacksonValue class.
  • This tutorial expose 2 endpoint rest API:
    . `/api/books`: Get books
    . `/api/books/{id}`: Get book detail by id
    Detail in class BookRestApi.java:

@RestController
@RequestMapping("/api")
public class BookRestApi {

    private final BookService bookService;

    public BookRestApi(BookService bookService) {
        this.bookService = bookService;
    }

    @GetMapping("/books")
    public ResponseEntity getBooks() {

        final SimpleFilterProvider filterProvider = new SimpleFilterProvider();
        filterProvider.addFilter("bookFilter", SimpleBeanPropertyFilter.serializeAllExcept("quantity"));
        filterProvider.addFilter("authorFilter", SimpleBeanPropertyFilter.serializeAllExcept("gender"));

        final List books = this.bookService.getBooks();

        final MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(books);
        mappingJacksonValue.setFilters(filterProvider);

        return new ResponseEntity(mappingJacksonValue, HttpStatus.OK);
    }

    @GetMapping("/books/{id}")
    public ResponseEntity getBook(@PathVariable("id") Long id) {

        final Optional book = bookService.getBook(id);
        if (book.isPresent()) {

            final SimpleFilterProvider filterProvider = new SimpleFilterProvider();
            filterProvider.addFilter("bookFilter", SimpleBeanPropertyFilter.serializeAllExcept("quantity"));
            filterProvider.addFilter("authorFilter", SimpleBeanPropertyFilter.serializeAllExcept("gender"));

            final MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(book.get());
            mappingJacksonValue.setFilters(filterProvider);

            return new ResponseEntity(mappingJacksonValue, HttpStatus.OK);

        } else {
            return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }
    }

}

Make sure MappingJacksonValue is the return type of the method.

Now Spring will take care of ignoring the fields mentioned in SimpleBeanPropertyFilter on serialization.

Now we can verify them by 2 unit test in class AppTests.java


    @Test
    void test_getBooks_IgnoreProperties_quantity_and_gender() throws Exception {
        mvc.perform(get("/api/books").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
            .andExpect(jsonPath("$.[*].name").value(hasItem("Spring in Action")))
            .andExpect(jsonPath("$.[*].price").value(hasItem(1000.0)))
            .andExpect(jsonPath("$.[*].quantity").doesNotExist())
            .andExpect(jsonPath("$.[*].authors.[*].gender").doesNotExist());
    }

    @Test
    void test_getBooks_detail_IgnoreProperties_quantity_and_gender() throws Exception {

        final Long id = bookRepository.findAll().get(0).getId();

        mvc.perform(get("/api/books/" + id).accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
            .andExpect(jsonPath("$.quantity").doesNotExist())
            .andExpect(jsonPath("$.authors.[*].gender").doesNotExist());
    }

 

-> Run application to verify: mvn clean spring-boot:run
. http://localhost:8080/api/books
. http://localhost:8080/api/books/{id}

-> Run unit test: mvn clean test

As always, the complete code examples used in this article are available over on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *