diff --git a/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java b/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java index 5197286d0..05d446ad7 100644 --- a/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java +++ b/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java @@ -52,6 +52,7 @@ import com.jayway.jsonpath.spi.mapper.MappingProvider; * {@link MethodInterceptorFactory} to create a {@link MethodInterceptor} that will * * @author Oliver Gierke + * @author Mark Paluch * @soundtrack Jeff Coffin - Fruitcake (The Inside Of The Outside) * @since 1.13 */ @@ -152,7 +153,9 @@ public class JsonProjectingMethodInterceptorFactory implements MethodInterceptor if (returnType.getRequiredActualType().getType().isInterface()) { List result = context.read(jsonPath); - return result.isEmpty() ? null : result.get(0); + Object nested = result.isEmpty() ? null : result.get(0); + + return isCollectionResult && !(nested instanceof Collection) ? result : nested; } type = isCollectionResult && JsonPath.isPathDefinite(jsonPath) diff --git a/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java b/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java index 45e33a623..44112fea5 100755 --- a/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java @@ -22,6 +22,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Set; @@ -38,6 +39,7 @@ import com.jayway.jsonpath.spi.mapper.MappingProvider; * Unit tests for {@link JsonProjectingMethodInterceptorFactory}. * * @author Oliver Gierke + * @author Mark Paluch * @since 1.13 * @soundtrack Richard Spaven - Assemble (Whole Other*) */ @@ -51,7 +53,8 @@ class JsonProjectingMethodInterceptorFactoryUnitTests { String json = "{\"firstname\" : \"Dave\", "// + "\"address\" : { \"zipCode\" : \"01097\", \"city\" : \"Dresden\" }," // - + "\"addresses\" : [ { \"zipCode\" : \"01097\", \"city\" : \"Dresden\" }]" + " }"; + + "\"addresses\" : [ { \"zipCode\" : \"01097\", \"city\" : \"Dresden\" }, { \"zipCode\" : \"69469\", \"city\" : \"Weinheim\" }]" + + " }"; SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); @@ -97,6 +100,13 @@ class JsonProjectingMethodInterceptorFactoryUnitTests { assertThat(customer.getAddressProjections().get(0).getZipCode()).isEqualTo("01097"); } + @Test // gh-2270 + void nestedProjectionCollectionShouldContainMultipleElements() { + assertThat(customer.getAddressProjections()).hasSize(2); + assertThat(customer.getAddressProjections().get(0).getZipCode()).isEqualTo("01097"); + assertThat(customer.getAddressProjections().get(1).getZipCode()).isEqualTo("69469"); + } + @Test // DATCMNS-885 void accessPropertyThatUsesJsonPathProjectionInTurn() { assertThat(customer.getAnotherAddressProjection().getZipCodeButNotCity()).isEqualTo("01097"); @@ -107,7 +117,7 @@ class JsonProjectingMethodInterceptorFactoryUnitTests { List projections = customer.getAnotherAddressProjections(); - assertThat(projections).hasSize(1); + assertThat(projections).hasSize(2); assertThat(projections.get(0).getZipCodeButNotCity()).isEqualTo("01097"); } @@ -133,7 +143,7 @@ class JsonProjectingMethodInterceptorFactoryUnitTests { void accessNestedFields() { assertThat(customer.getNestedCity()).isEqualTo("Dresden"); - assertThat(customer.getNestedCities()).hasSize(2); + assertThat(customer.getNestedCities()).hasSize(3); } @Test // DATACMNS-1144 @@ -146,6 +156,18 @@ class JsonProjectingMethodInterceptorFactoryUnitTests { assertThat(customer.getName().getSomeName()).isEqualTo(customer.getName().getFirstname()); } + @Test // gh-2270 + void shouldProjectOnArray() { + + String json = "[ { \"creationDate\": 1610111331413, \"changeDate\": 1610111332160, \"person\": { \"caption\": \"Test2 TEST2\", \"firstName\": \"Test2\", \"lastName\": \"Test2\" } }, " + + "{ \"creationDate\": 1609775450502, \"changeDate\": 1609775451333, \"person\": { \"caption\": \"Test TEST\", \"firstName\": \"Test\", \"lastName\": \"Test\" } }]"; + + UserPayload projection = projectionFactory.createProjection(UserPayload.class, + new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8))); + + assertThat(projection.users()).hasSize(2); + } + interface Customer { String getFirstname(); @@ -218,4 +240,18 @@ class JsonProjectingMethodInterceptorFactoryUnitTests { static class Address { private String zipCode, city; } + + @ProjectedPayload + interface UserPayload { + + @JsonPath("$..person") + List users(); + + interface Users { + + public String getFirstName(); + + public String getLastName(); + } + } }