Fix deserialising from Pageable.

This commit is contained in:
Olga MaciaszekSharma
2024-02-14 15:31:38 +01:00
parent 9930c63abd
commit a8867cfafe
5 changed files with 304 additions and 29 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,6 +57,18 @@ public class PageJacksonModule extends Module {
@Override
public void setupModule(SetupContext context) {
context.setMixInAnnotations(Page.class, PageMixIn.class);
context.setMixInAnnotations(Pageable.class, PageableMixIn.class);
}
private static PageRequest buildPageRequest(int number, int size, Sort sort) {
PageRequest pageRequest;
if (sort != null) {
pageRequest = PageRequest.of(number, size, sort);
}
else {
pageRequest = PageRequest.of(number, size);
}
return pageRequest;
}
@JsonDeserialize(as = SimplePageImpl.class)
@@ -65,24 +77,29 @@ public class PageJacksonModule extends Module {
}
@JsonDeserialize(as = SimplePageable.class)
@JsonIgnoreProperties(ignoreUnknown = true)
private interface PageableMixIn {
}
static class SimplePageImpl<T> implements Page<T> {
private final Page<T> delegate;
SimplePageImpl(@JsonProperty("content") List<T> content, @JsonProperty("number") int number,
@JsonProperty("size") int size, @JsonProperty("totalElements") @JsonAlias({ "total-elements",
"total_elements", "totalelements", "TotalElements" }) long totalElements,
SimplePageImpl(@JsonProperty("content") List<T> content, @JsonProperty("pageable") Pageable pageable,
@JsonProperty("number") @JsonAlias("pageNumber") int number,
@JsonProperty("size") @JsonAlias("pageSize") int size,
@JsonProperty("totalElements") @JsonAlias({ "total-elements", "total_elements", "totalelements",
"TotalElements", "total" }) long totalElements,
@JsonProperty("sort") Sort sort) {
if (size > 0) {
PageRequest pageRequest;
if (sort != null) {
pageRequest = PageRequest.of(number, size, sort);
}
else {
pageRequest = PageRequest.of(number, size);
}
PageRequest pageRequest = buildPageRequest(number, size, sort);
delegate = new PageImpl<>(content, pageRequest, totalElements);
}
else if (pageable != null && pageable.getPageSize() > 0) {
delegate = new PageImpl<>(content, pageable, totalElements);
}
else {
delegate = new PageImpl<>(content);
}
@@ -213,4 +230,60 @@ public class PageJacksonModule extends Module {
}
static class SimplePageable implements Pageable {
private final PageRequest delegate;
SimplePageable(@JsonProperty("pageNumber") int number, @JsonProperty("pageSize") int size,
@JsonProperty("sort") Sort sort) {
delegate = buildPageRequest(number, size, sort);
}
@Override
public int getPageNumber() {
return delegate.getPageNumber();
}
@Override
public int getPageSize() {
return delegate.getPageSize();
}
@Override
public long getOffset() {
return delegate.getOffset();
}
@Override
public Sort getSort() {
return delegate.getSort();
}
@Override
public Pageable next() {
return delegate.next();
}
@Override
public Pageable previousOrFirst() {
return delegate.previousOrFirst();
}
@Override
public Pageable first() {
return delegate.first();
}
@Override
public Pageable withPage(int pageNumber) {
return delegate.withPage(pageNumber);
}
@Override
public boolean hasPrevious() {
return delegate.hasPrevious();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,10 +34,11 @@ import feign.codec.EncodeException;
import org.springframework.data.domain.Sort;
/**
* This class provides provides support for serializing and deserializing for Spring
* {@link Sort} object.
* This class provides support for serializing and deserializing for Spring {@link Sort}
* object.
*
* @author Can Bezmen
* @author Olga Maciaszek-Sharma
*/
public class SortJsonComponent {
@@ -72,13 +73,11 @@ public class SortJsonComponent {
TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
if (treeNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) treeNode;
List<Sort.Order> orders = new ArrayList<>();
for (JsonNode jsonNode : arrayNode) {
Sort.Order order = new Sort.Order(Sort.Direction.valueOf(jsonNode.get("direction").textValue()),
jsonNode.get("property").textValue());
orders.add(order);
}
return Sort.by(orders);
return toSort(arrayNode);
}
else if (treeNode.get("orders") != null && treeNode.get("orders").isArray()) {
ArrayNode arrayNode = (ArrayNode) treeNode.get("orders");
return toSort(arrayNode);
}
return null;
}
@@ -88,6 +87,16 @@ public class SortJsonComponent {
return Sort.class;
}
private static Sort toSort(ArrayNode arrayNode) {
List<Sort.Order> orders = new ArrayList<>();
for (JsonNode jsonNode : arrayNode) {
Sort.Order order = new Sort.Order(Sort.Direction.valueOf(jsonNode.get("direction").textValue()),
jsonNode.get("property").textValue());
orders.add(order);
}
return Sort.by(orders);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.cloud.openfeign.support;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -29,6 +31,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import static org.assertj.core.api.Assertions.assertThat;
@@ -40,12 +43,12 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Pedro Mendes
* @author Nikita Konev
*/
public class PageJacksonModuleTests {
class PageJacksonModuleTests {
private static ObjectMapper objectMapper;
@BeforeAll
public static void initialize() {
static void initialize() {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new PageJacksonModule());
objectMapper.registerModule(new SortJacksonModule());
@@ -53,7 +56,7 @@ public class PageJacksonModuleTests {
@ParameterizedTest
@ValueSource(strings = { "totalElements", "total-elements", "total_elements", "totalelements", "TotalElements" })
public void deserializePage(String totalElements) throws JsonProcessingException {
void deserializePage(String totalElements) throws JsonProcessingException {
// Given
String pageJson = "{\"content\":[\"A name\"], \"number\":1, \"size\":2, \"" + totalElements + "\": 3}";
// When
@@ -67,8 +70,24 @@ public class PageJacksonModuleTests {
assertThat(result.getPageable().getPageNumber()).isEqualTo(1);
}
@SuppressWarnings("DataFlowIssue")
@ParameterizedTest
@ValueSource(strings = {"./src/test/resources/withPageable.json", "./src/test/resources/withoutPageable.json"})
void deserializePageFromFileWithPageable(String filePath) throws IOException {
File file = new File(filePath);
Page<?> result = objectMapper.readValue(file, Page.class);
assertThat(result.getTotalElements()).isEqualTo(11);
assertThat(result.getContent()).hasSize(10);
assertThat(result.getPageable().getPageNumber()).isEqualTo(0);
assertThat(result.getPageable().getSort().getOrderFor("lastName")
.getDirection()).isEqualTo(Sort.Direction.DESC);
}
@Test
public void serializeAndDeserializeEmpty() throws JsonProcessingException {
void serializeAndDeserializeEmpty() throws JsonProcessingException {
// Given
PageImpl<Object> objects = new PageImpl<>(new ArrayList<>(), Pageable.ofSize(1), 0);
String pageJson = objectMapper.writeValueAsString(objects);
@@ -81,7 +100,7 @@ public class PageJacksonModuleTests {
}
@Test
public void serializeAndDeserializeFilledMultiple() throws JsonProcessingException {
void serializeAndDeserializeFilledMultiple() throws JsonProcessingException {
// Given
ArrayList<Object> pageElements = new ArrayList<>();
pageElements.add("first element");
@@ -104,7 +123,7 @@ public class PageJacksonModuleTests {
}
@Test
public void serializeAndDeserializeEmptyCascade() throws JsonProcessingException {
void serializeAndDeserializeEmptyCascade() throws JsonProcessingException {
// Given
PageImpl<Object> objects = new PageImpl<>(new ArrayList<>(), Pageable.ofSize(1), 0);
String pageJson = objectMapper.writeValueAsString(objects);
@@ -123,7 +142,7 @@ public class PageJacksonModuleTests {
}
@Test
public void serializeAndDeserializeFilledMultipleCascade() throws JsonProcessingException {
void serializeAndDeserializeFilledMultipleCascade() throws JsonProcessingException {
// Given
ArrayList<Object> pageElements = new ArrayList<>();
pageElements.add("first element in cascaded serialization");

View File

@@ -0,0 +1,90 @@
{
"content": [
{
"id": 3,
"lastName": "Williams",
"firstName": "Thomas",
"email": "w.t@my.domain.com"
},
{
"id": 1,
"lastName": "Smith",
"firstName": "James",
"email": "s.j@my.domain.com"
},
{
"id": 11,
"lastName": "Scott",
"firstName": "Steven",
"email": "s.s@my.domain.com"
},
{
"id": 8,
"lastName": "Rodriguez",
"firstName": "Daniel",
"email": "r.d@my.domain.com"
},
{
"id": 9,
"lastName": "Martinez",
"firstName": "Robert",
"email": "m.r@my.domain.com"
},
{
"id": 5,
"lastName": "Jones",
"firstName": "James",
"email": "j.j@my.domain.com"
},
{
"id": 2,
"lastName": "Johnson",
"firstName": "Robert",
"email": "j.r@my.domain.com"
},
{
"id": 6,
"lastName": "Garcia",
"firstName": "William",
"email": "g.w@my.domain.com"
},
{
"id": 7,
"lastName": "Davis",
"firstName": "Richard",
"email": "d.r@my.domain.com"
},
{
"id": 4,
"lastName": "Brown",
"firstName": "Paul",
"email": "b.p@my.domain.com"
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 10,
"sort": {
"orders": [
{
"direction": "DESC",
"property": "lastName",
"ignoreCase": false,
"nullHandling": "NATIVE",
"ascending": false,
"descending": true
}
],
"sorted": true,
"empty": false,
"unsorted": false
},
"paged": true,
"unpaged": false
},
"total": 11,
"last": false,
"first": true,
"empty": false
}

View File

@@ -0,0 +1,84 @@
{
"content": [
{
"id": 3,
"lastName": "Williams",
"firstName": "Thomas",
"email": "w.t@my.domain.com"
},
{
"id": 1,
"lastName": "Smith",
"firstName": "James",
"email": "s.j@my.domain.com"
},
{
"id": 11,
"lastName": "Scott",
"firstName": "Steven",
"email": "s.s@my.domain.com"
},
{
"id": 8,
"lastName": "Rodriguez",
"firstName": "Daniel",
"email": "r.d@my.domain.com"
},
{
"id": 9,
"lastName": "Martinez",
"firstName": "Robert",
"email": "m.r@my.domain.com"
},
{
"id": 5,
"lastName": "Jones",
"firstName": "James",
"email": "j.j@my.domain.com"
},
{
"id": 2,
"lastName": "Johnson",
"firstName": "Robert",
"email": "j.r@my.domain.com"
},
{
"id": 6,
"lastName": "Garcia",
"firstName": "William",
"email": "g.w@my.domain.com"
},
{
"id": 7,
"lastName": "Davis",
"firstName": "Richard",
"email": "d.r@my.domain.com"
},
{
"id": 4,
"lastName": "Brown",
"firstName": "Paul",
"email": "b.p@my.domain.com"
}
],
"sort": {
"orders": [
{
"direction": "DESC",
"property": "lastName",
"ignoreCase": false,
"nullHandling": "NATIVE",
"ascending": false,
"descending": true
}
],
"sorted": true,
"empty": false,
"unsorted": false
},
"size": 2,
"total": 11,
"last": false,
"first": true,
"empty": false
}