Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Pagination returns no results with JSONView

I am using Spring data pagination in my REST Controller and returning Paged entity. I would like to control the data returned as JSON with the help of JSONViews.

I am able to achieve the result when I return a single object. But when I return Page, I am receiving blank JSON as response.

Following is my method signature.

@JsonView(TravelRequestView.MyRequests.class)
@RequestMapping("/travel/requests")
public Page<TravelRequest> getUserTravelRequests(
            @RequestParam("ps") int pageSize, @RequestParam("p") int page,
            @RequestParam(defaultValue = "", value = "q") String searchString)

I am able to receive response when I remove @JsonView annotation.

like image 963
Pavan Andhukuri Avatar asked Aug 10 '15 07:08

Pavan Andhukuri


2 Answers

If you are using spring-boot, then another simpler solution would be add the following to application.yml

spring:
  jackson:
    mapper:
      DEFAULT_VIEW_INCLUSION: true

or application.properties

spring.jackson.mapper.DEFAULT_VIEW_INCLUSION=true

With this approach, we have the advantage of retaining the ObjectMapper managed by Spring Container and not creating a new ObjectMapper. So once we use the spring managed ObjectMapper, then any Custom Serialzers we define will still continue to work e.g CustomDateSerialzer

Reference: http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html

like image 65
Narasimha Avatar answered Oct 27 '22 12:10

Narasimha


Setting DEFAULT_VIEW_INCLUSION has a global effect, while all we need is to be able to serialize a Page object. The following code will register a serializer for Page, and is a simple change to your code:

@Bean
public Module springDataPageModule() {
    return new SimpleModule().addSerializer(Page.class, new JsonSerializer<Page>() {
        @Override
        public void serialize(Page value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeStartObject();
            gen.writeNumberField("totalElements",value.getTotalElements());
            gen.writeNumberField("totalPages", value.getTotalPages());
            gen.writeNumberField("number", value.getNumber());
            gen.writeNumberField("size", value.getSize());
            gen.writeBooleanField("first", value.isFirst());
            gen.writeBooleanField("last", value.isLast());
            gen.writeFieldName("content");
            serializers.defaultSerializeValue(value.getContent(),gen);
            gen.writeEndObject();
        }
    });
}

Another (arguably more elegant) solution is to register the following ResponseBodyAdvice. It will make sure your REST endpoint will still return a JSON array, and set a HTTP header 'X-Has-Next-Page' to indicate whether there is more data. The advantages are: 1) No extra count(*) query to your DB (single query) 2) Response is more elegant, since it returns a JSON array

/**
 * ResponseBodyAdvice to support Spring data Slice object in JSON responses.
 * If the value is a slice, we'll write the List as an array, and add a header to the HTTP response
 *
 * @author blagerweij
 */
@ControllerAdvice
public class SliceResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Slice) {
            Slice slice = (Slice) body;
            response.getHeaders().add("X-Has-Next-Page", String.valueOf(slice.hasNext()));
            return slice.getContent();
        }
        return body;
    }
}
like image 27
blagerweij Avatar answered Oct 27 '22 11:10

blagerweij