diff --git a/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java b/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java index 58a43328f..64bf85db3 100644 --- a/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java +++ b/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java @@ -22,6 +22,8 @@ import net.minidev.json.JSONObject; import java.io.InputStream; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; import java.util.Map; import org.aopalliance.intercept.MethodInterceptor; @@ -38,6 +40,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.Option; import com.jayway.jsonpath.ParseContext; import com.jayway.jsonpath.TypeRef; import com.jayway.jsonpath.spi.mapper.MappingProvider; @@ -63,6 +66,7 @@ public class JsonProjectingMethodInterceptorFactory implements MethodInterceptor Assert.notNull(mappingProvider, "MappingProvider must not be null!"); Configuration build = Configuration.builder()// + .options(Option.ALWAYS_RETURN_LIST)// .mappingProvider(mappingProvider)// .build(); @@ -131,8 +135,24 @@ public class JsonProjectingMethodInterceptorFactory implements MethodInterceptor ResolvableType type = ResolvableType.forMethodReturnType(method); String jsonPath = getJsonPath(method); - return !returnType.getActualType().getType().isInterface() ? context.read(jsonPath, new ResolvableTypeRef(type)) - : context.read(jsonPath); + if (returnType.getActualType().getType().isInterface()) { + + List result = context.read(jsonPath); + return result.isEmpty() ? null : result.get(0); + } + + boolean isCollectionResult = Collection.class.isAssignableFrom(type.getRawClass()); + type = isCollectionResult ? type : ResolvableType.forClassWithGenerics(List.class, type); + type = isCollectionResult && JsonPath.isPathDefinite(jsonPath) + ? ResolvableType.forClassWithGenerics(List.class, type) : type; + + List result = (List) context.read(jsonPath, new ResolvableTypeRef(type)); + + if (isCollectionResult && JsonPath.isPathDefinite(jsonPath)) { + result = (List) result.get(0); + } + + return isCollectionResult ? result : result.isEmpty() ? null : result.get(0); } /** diff --git a/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java b/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java index 6ab4a7f17..26f21cdf7 100644 --- a/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java @@ -163,6 +163,16 @@ public class JsonProjectingMethodInterceptorFactoryUnitTests { assertThat(name.getFirstname(), is("Dave")); } + /** + * @see DATACMNS-885 + */ + @Test + public void accessNestedFields() { + + assertThat(customer.getNestedCity(), is("Dresden")); + assertThat(customer.getNestedCities(), hasSize(2)); + } + interface Customer { String getFirstname(); @@ -194,6 +204,12 @@ public class JsonProjectingMethodInterceptorFactoryUnitTests { @JsonPath("$.address") Set getAnotherAddressProjectionAsCollection(); + + @JsonPath("$..city") + String getNestedCity(); + + @JsonPath("$..city") + List getNestedCities(); } interface AddressProjection {