This issue appeared in Spring-Data release 2. In latest version 1.13.9 (and older) it works fine.
Controller code:
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
@RequestMapping(value = "sorttest", method = RequestMethod.GET)
public Page<Integer> getDummy() {
return new PageImpl<>(Collections.singletonList(1), new PageRequest(0, 5, new Sort("asdf")), 1);
}
}
Same for Spring-Data 2 style:
Pageable pageable = PageRequest.of(0, 10, new Sort(Sort.Direction.ASC, "asd"));
PageImpl<Integer> page = new PageImpl<Integer>(Lists.newArrayList(1,2,3), pageable, 3);
return page;
Configuration:
@SpringBootApplication
@EnableWebMvc
@EnableSpringDataWebSupport
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also tried simple Spring application without Spring Boot with Java config as well as with XML config. Result is same:
{
"content": [
1
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false
},
"offset": 0,
"pageSize": 5,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"totalElements": 1,
"last": true,
"totalPages": 1,
"size": 5,
"number": 0,
"sort": {
"sorted": true,
"unsorted": false
},
"numberOfElements": 1,
"first": true
}
If I change Spring-Data version to 1.X I'm getting correct JSON response for sorting object:
{
"content": [
1
],
"totalElements": 1,
"totalPages": 1,
"last": true,
"size": 5,
"number": 0,
"sort": [
{
"direction": "ASC",
"property": "asdf",
"ignoreCase": false,
"nullHandling": "NATIVE",
"ascending": true,
"descending": false
}
],
"numberOfElements": 1,
"first": true
}
It seems I tried everything, I didn't find any notification on sort changes in changelog, I didn't find such issue in Spring JIRA.
So the question is how do I get with spring-data 2.X libs JSON with sorting like:
"sort": [
{
"direction": "ASC",
"property": "asdf",
"ignoreCase": false,
"nullHandling": "NATIVE",
"ascending": true,
"descending": false
}
]
instead of:
"sort": {
"sorted": true,
"unsorted": false
}
1. Overview Pagination is often helpful when we have a large dataset and we want to present it to the user in smaller chunks. Also, we often need to sort that data by some criteria while paging. In this tutorial, we'll learn how to easily paginate and sort using Spring Data JPA.
We can create a PageRequest object by passing in the requested page number and the page size. In Spring MVC, we can also choose to obtain the Pageable instance in our controller using Spring Data Web Support.
We can do that by passing the sorting details into our PageRequest object itself: Based on our sorting requirements, we can specify the sort fields and the sort direction while creating our PageRequest instance. As usual, we can then pass this Pageable type instance to the repository's method.
@Oleg Danyliuk
I have encountered the same issue as you do and found your response useful however here I provide a shortest answer.
As you said, it is needed to create a custom serializer for the Sort class.
But then, you only need to annotate the JsonSerializer class with @JsonComponent
to register it with Jackson.
Solution
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.data.domain.Sort;
import java.io.IOException;
@JsonComponent
public class SortJsonSerializer extends JsonSerializer<Sort> {
@Override
public void serialize(Sort value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartArray();
value.iterator().forEachRemaining(v -> {
try {
gen.writeObject(v);
} catch (IOException e) {
e.printStackTrace();
}
});
gen.writeEndArray();
}
@Override
public Class<Sort> handledType() {
return Sort.class;
}
}
References
I've solved an issue adding my custom serializator:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.data.domain.Sort;
import java.io.IOException;
public class SortJsonSerializer extends JsonSerializer<Sort> {
@Override
public void serialize(Sort orders, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartArray();
orders.iterator().forEachRemaining(o -> {
try {
jsonGenerator.writeObject(o);
} catch (IOException e) {
e.printStackTrace();
}
});
jsonGenerator.writeEndArray();
}
@Override
public Class<Sort> handledType() {
return Sort.class;
}
}
And to make Spring use it I override extendMessageConverters:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.xxx.ws"})
@EnableSpringDataWebSupport
public class SpringWebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
SortHandlerMethodArgumentResolver sortResolver = new SortHandlerMethodArgumentResolver();
// For sorting resolution alone
argumentResolvers.add(sortResolver);
PageableHandlerMethodArgumentResolver pageableResolver = new PageableHandlerMethodArgumentResolver(sortResolver);
pageableResolver.setMaxPageSize(10000);
// For sorting resolution encapsulated inside a pageable
argumentResolvers.add(pageableResolver);
argumentResolvers.add(new CurrentUserArgumentResolver());
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
SimpleModule module = new SimpleModule();
module.addSerializer(Sort.class, new SortJsonSerializer());
return new Jackson2ObjectMapperBuilder()
.findModulesViaServiceLoader(true)
.modulesToInstall(module);
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//First converters added in WebMvcConfigurationSupport.addDefaultHttpMessageConverters and then we add our behaviour here
Jackson2ObjectMapperBuilder builder = jackson2ObjectMapperBuilder();
for (int i=0; i<converters.size(); i++) {
if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
converters.set(i, new MappingJackson2HttpMessageConverter(builder.build()));
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With