From 21c53ddd54391734e4f24beb3ed4074bc7798661 Mon Sep 17 00:00:00 2001 From: weslyvinicius Date: Fri, 20 Feb 2026 15:01:38 +0000 Subject: [PATCH 1/3] Fix #1324: Add @JsonAlias support to SimplePageable for SNAKE_CASE compatibility Signed-off-by: weslyvinicius --- .../openfeign/support/PageJacksonModule.java | 6 +- .../support/PageJacksonModuleTests.java | 80 +++++++++++++++++++ .../resources/withPageableAliasHyphen.json | 77 ++++++++++++++++++ .../resources/withPageableAliasLowercase.json | 72 +++++++++++++++++ .../withPageableAliasPascalCase.json | 77 ++++++++++++++++++ .../withPageableAliasUnderscore.json | 77 ++++++++++++++++++ 6 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-openfeign-core/src/test/resources/withPageableAliasHyphen.json create mode 100644 spring-cloud-openfeign-core/src/test/resources/withPageableAliasLowercase.json create mode 100644 spring-cloud-openfeign-core/src/test/resources/withPageableAliasPascalCase.json create mode 100644 spring-cloud-openfeign-core/src/test/resources/withPageableAliasUnderscore.json diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java index dc38c598b..cff84f68f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java @@ -242,8 +242,10 @@ static class SimplePageable implements Pageable { private final PageRequest delegate; - SimplePageable(@JsonProperty("pageNumber") int number, @JsonProperty("pageSize") int size, - @JsonProperty("sort") Sort sort) { + SimplePageable( + @JsonProperty("pageNumber") @JsonAlias({"page-number", "page_number", "pagenumber", "PageNumber"}) int number, + @JsonProperty("pageSize") @JsonAlias({"page-size", "page_size", "pagesize", "PageSize"}) int size, + @JsonProperty("sort") Sort sort) { delegate = buildPageRequest(number, size, sort); } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java index 635913d99..ad46156fc 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -188,4 +189,83 @@ void serializeAndDeserializeFilledMultipleCascade() throws JsonProcessingExcepti assertThat(cascadedResult.getPageable().getPageNumber()).isEqualTo(6); } + @Test + void deserializePageableWithHyphenatedAlias() throws IOException { + // Given + ObjectMapper snakeCaseMapper = objectMapper.copy(); + snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE); + File file = new File("./src/test/resources/withPageableAliasHyphen.json"); + // When + Page result = objectMapper.readValue(file, Page.class); + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalElements()).isEqualTo(15); + assertThat(result.getContent()).hasSize(10); + assertThat(result.getPageable()).isNotNull(); + assertThat(result.getPageable().getPageNumber()).isEqualTo(2); + assertThat(result.getPageable().getPageSize()).isEqualTo(3); + assertThat(result.getPageable().getSort().getOrderFor("firstName").getDirection()) + .isEqualTo(Sort.Direction.ASC); + } + + @Test + void deserializePageableWithUnderscoreAlias() throws IOException { + // Given + ObjectMapper snakeCaseMapper = objectMapper.copy(); + snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + File file = new File("./src/test/resources/withPageableAliasUnderscore.json"); + + // When + Page result = snakeCaseMapper.readValue(file, Page.class); + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalElements()).isEqualTo(10); + assertThat(result.getContent()).hasSize(10); + assertThat(result.getPageable()).isNotNull(); + assertThat(result.getPageable().getPageNumber()).isEqualTo(1); + assertThat(result.getPageable().getPageSize()).isEqualTo(2); + assertThat(result.getPageable().getSort().getOrderFor("lastName").getDirection()) + .isEqualTo(Sort.Direction.DESC); + } + + @Test + void deserializePageableWithLowercaseAlias() throws IOException { + // Given + ObjectMapper snakeCaseMapper = objectMapper.copy(); + snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CASE); + + File file = new File("./src/test/resources/withPageableAliasLowercase.json"); + + // When + Page result = objectMapper.readValue(file, Page.class); + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalElements()).isEqualTo(8); + assertThat(result.getContent()).hasSize(10); + assertThat(result.getPageable()).isNotNull(); + assertThat(result.getPageable().getPageNumber()).isEqualTo(0); + assertThat(result.getPageable().getPageSize()).isEqualTo(4); + assertThat(result.getPageable().getSort()).isEqualTo(Sort.unsorted()); + } + + @Test + void deserializePageableWithPascalCaseAlias() throws IOException { + // Given + ObjectMapper snakeCaseMapper = objectMapper.copy(); + snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); + File file = new File("./src/test/resources/withPageableAliasPascalCase.json"); + + // When + Page result = objectMapper.readValue(file, Page.class); + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalElements()).isEqualTo(20); + assertThat(result.getContent()).hasSize(10); + assertThat(result.getPageable()).isNotNull(); + assertThat(result.getPageable().getPageNumber()).isEqualTo(3); + assertThat(result.getPageable().getPageSize()).isEqualTo(2); + assertThat(result.getPageable().getSort().getOrderFor("firstName").getDirection()) + .isEqualTo(Sort.Direction.ASC); + } + } diff --git a/spring-cloud-openfeign-core/src/test/resources/withPageableAliasHyphen.json b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasHyphen.json new file mode 100644 index 000000000..433c1ff0a --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasHyphen.json @@ -0,0 +1,77 @@ +{ + "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": { + "page-number": 2, + "page-size": 3, + "sort": { + "orders": [ + { + "direction": "ASC", + "property": "firstName" + } + ] + } + }, + "totalElements": 15 +} diff --git a/spring-cloud-openfeign-core/src/test/resources/withPageableAliasLowercase.json b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasLowercase.json new file mode 100644 index 000000000..73c9ae757 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasLowercase.json @@ -0,0 +1,72 @@ +{ + "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": 4, + "sort": { + "orders": [] + } + }, + "totalElements": 8 +} diff --git a/spring-cloud-openfeign-core/src/test/resources/withPageableAliasPascalCase.json b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasPascalCase.json new file mode 100644 index 000000000..e36bbb311 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasPascalCase.json @@ -0,0 +1,77 @@ +{ + "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": 3, + "PageSize": 2, + "sort": { + "orders": [ + { + "direction": "ASC", + "property": "firstName" + } + ] + } + }, + "totalElements": 20 +} diff --git a/spring-cloud-openfeign-core/src/test/resources/withPageableAliasUnderscore.json b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasUnderscore.json new file mode 100644 index 000000000..3667c9447 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/resources/withPageableAliasUnderscore.json @@ -0,0 +1,77 @@ +{ + "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": { + "page_number": 1, + "page_size": 2, + "sort": { + "orders": [ + { + "direction": "DESC", + "property": "lastName" + } + ] + } + }, + "totalElements": 10 +} From 926df9228df1c5b5d6d5b14377ce43a3ca5d5f9b Mon Sep 17 00:00:00 2001 From: weslyvinicius Date: Fri, 20 Feb 2026 15:16:58 +0000 Subject: [PATCH 2/3] Fix #1324: Add @JsonAlias support to SimplePageable for SNAKE_CASE compatibility Signed-off-by: weslyvinicius --- .../openfeign/support/PageJacksonModule.java | 7 ++++--- .../support/PageJacksonModuleTests.java | 18 +++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java index cff84f68f..c8fe45e10 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java @@ -243,9 +243,10 @@ static class SimplePageable implements Pageable { private final PageRequest delegate; SimplePageable( - @JsonProperty("pageNumber") @JsonAlias({"page-number", "page_number", "pagenumber", "PageNumber"}) int number, - @JsonProperty("pageSize") @JsonAlias({"page-size", "page_size", "pagesize", "PageSize"}) int size, - @JsonProperty("sort") Sort sort) { + @JsonProperty("pageNumber") @JsonAlias({ "page-number", "page_number", "pagenumber", + "PageNumber" }) int number, + @JsonProperty("pageSize") @JsonAlias({ "page-size", "page_size", "pagesize", "PageSize" }) int size, + @JsonProperty("sort") Sort sort) { delegate = buildPageRequest(number, size, sort); } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java index ad46156fc..159a3d0d9 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java @@ -192,11 +192,11 @@ void serializeAndDeserializeFilledMultipleCascade() throws JsonProcessingExcepti @Test void deserializePageableWithHyphenatedAlias() throws IOException { // Given - ObjectMapper snakeCaseMapper = objectMapper.copy(); - snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE); + ObjectMapper kebabOjectMapepr = objectMapper.copy(); + kebabOjectMapepr.setPropertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE); File file = new File("./src/test/resources/withPageableAliasHyphen.json"); // When - Page result = objectMapper.readValue(file, Page.class); + Page result = kebabOjectMapepr.readValue(file, Page.class); // Then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(15); @@ -231,13 +231,13 @@ void deserializePageableWithUnderscoreAlias() throws IOException { @Test void deserializePageableWithLowercaseAlias() throws IOException { // Given - ObjectMapper snakeCaseMapper = objectMapper.copy(); - snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CASE); + ObjectMapper lowerCaseMapper = objectMapper.copy(); + lowerCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CASE); File file = new File("./src/test/resources/withPageableAliasLowercase.json"); // When - Page result = objectMapper.readValue(file, Page.class); + Page result = lowerCaseMapper.readValue(file, Page.class); // Then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(8); @@ -251,12 +251,12 @@ void deserializePageableWithLowercaseAlias() throws IOException { @Test void deserializePageableWithPascalCaseAlias() throws IOException { // Given - ObjectMapper snakeCaseMapper = objectMapper.copy(); - snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); + ObjectMapper upperCamelCaseMapper = objectMapper.copy(); + upperCamelCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); File file = new File("./src/test/resources/withPageableAliasPascalCase.json"); // When - Page result = objectMapper.readValue(file, Page.class); + Page result = upperCamelCaseMapper.readValue(file, Page.class); // Then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(20); From 7ac14f20e388076b1a8abc316316a4f089cf2fae Mon Sep 17 00:00:00 2001 From: weslyvinicius Date: Fri, 20 Feb 2026 17:01:36 +0000 Subject: [PATCH 3/3] Fix typo in kebabObjectMapper variable name in test Signed-off-by: weslyvinicius --- .../support/PageJacksonModuleTests.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java index 159a3d0d9..6c06f05aa 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java @@ -192,11 +192,11 @@ void serializeAndDeserializeFilledMultipleCascade() throws JsonProcessingExcepti @Test void deserializePageableWithHyphenatedAlias() throws IOException { // Given - ObjectMapper kebabOjectMapepr = objectMapper.copy(); - kebabOjectMapepr.setPropertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE); + ObjectMapper kebabObjectMapepr = objectMapper.copy(); + kebabObjectMapepr.setPropertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE); File file = new File("./src/test/resources/withPageableAliasHyphen.json"); // When - Page result = kebabOjectMapepr.readValue(file, Page.class); + Page result = kebabObjectMapepr.readValue(file, Page.class); // Then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(15); @@ -211,12 +211,12 @@ void deserializePageableWithHyphenatedAlias() throws IOException { @Test void deserializePageableWithUnderscoreAlias() throws IOException { // Given - ObjectMapper snakeCaseMapper = objectMapper.copy(); - snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + ObjectMapper snakeCaseObjectMapper = objectMapper.copy(); + snakeCaseObjectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); File file = new File("./src/test/resources/withPageableAliasUnderscore.json"); // When - Page result = snakeCaseMapper.readValue(file, Page.class); + Page result = snakeCaseObjectMapper.readValue(file, Page.class); // Then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(10); @@ -231,13 +231,13 @@ void deserializePageableWithUnderscoreAlias() throws IOException { @Test void deserializePageableWithLowercaseAlias() throws IOException { // Given - ObjectMapper lowerCaseMapper = objectMapper.copy(); - lowerCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CASE); + ObjectMapper lowerCaseObjectMapper = objectMapper.copy(); + lowerCaseObjectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CASE); File file = new File("./src/test/resources/withPageableAliasLowercase.json"); // When - Page result = lowerCaseMapper.readValue(file, Page.class); + Page result = lowerCaseObjectMapper.readValue(file, Page.class); // Then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(8); @@ -251,12 +251,12 @@ void deserializePageableWithLowercaseAlias() throws IOException { @Test void deserializePageableWithPascalCaseAlias() throws IOException { // Given - ObjectMapper upperCamelCaseMapper = objectMapper.copy(); - upperCamelCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); + ObjectMapper upperCamelCaseObjectMapper = objectMapper.copy(); + upperCamelCaseObjectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); File file = new File("./src/test/resources/withPageableAliasPascalCase.json"); // When - Page result = upperCamelCaseMapper.readValue(file, Page.class); + Page result = upperCamelCaseObjectMapper.readValue(file, Page.class); // Then assertThat(result).isNotNull(); assertThat(result.getTotalElements()).isEqualTo(20);