diff --git a/src/main/java/org/springframework/data/web/JsonPath.java b/src/main/java/org/springframework/data/web/JsonPath.java index 968b17840..6e2bcbc29 100644 --- a/src/main/java/org/springframework/data/web/JsonPath.java +++ b/src/main/java/org/springframework/data/web/JsonPath.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 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,9 +34,10 @@ import java.lang.annotation.Target; public @interface JsonPath { /** - * The JSON Path expression to be evaluated when the annotated method is invoked. + * The JSON Path expressions to be evaluated when the annotated method is invoked. If multiple ones are defined, the + * value of the first one actually present in the payload will be returned. * * @return */ - String value(); + String[] value(); } diff --git a/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java b/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java index a122d1f1b..1fd64ead6 100644 --- a/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java +++ b/src/main/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 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. @@ -22,7 +22,9 @@ import net.minidev.json.JSONObject; import java.io.InputStream; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -43,6 +45,7 @@ import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.Option; import com.jayway.jsonpath.ParseContext; +import com.jayway.jsonpath.PathNotFoundException; import com.jayway.jsonpath.TypeRef; import com.jayway.jsonpath.spi.mapper.MappingProvider; @@ -135,27 +138,39 @@ public class JsonProjectingMethodInterceptorFactory implements MethodInterceptor Method method = invocation.getMethod(); TypeInformation returnType = ClassTypeInformation.fromReturnTypeOf(method); ResolvableType type = ResolvableType.forMethodReturnType(method); - String jsonPath = getJsonPath(method); - - if (returnType.getRequiredActualType().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)); + Iterable jsonPaths = getJsonPaths(method); - if (isCollectionResult && JsonPath.isPathDefinite(jsonPath)) { - result = (List) result.get(0); + for (String jsonPath : jsonPaths) { + + try { + + if (returnType.getRequiredActualType().getType().isInterface()) { + + List result = context.read(jsonPath); + return result.isEmpty() ? null : result.get(0); + } + + 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); + + } catch (PathNotFoundException o_O) { + // continue with next path + } } - return isCollectionResult ? result : result.isEmpty() ? null : result.get(0); + return null; } /** @@ -164,12 +179,16 @@ public class JsonProjectingMethodInterceptorFactory implements MethodInterceptor * @param method * @return */ - private static String getJsonPath(Method method) { + private static Collection getJsonPaths(Method method) { org.springframework.data.web.JsonPath annotation = AnnotationUtils.findAnnotation(method, org.springframework.data.web.JsonPath.class); - return annotation != null ? annotation.value() : "$.".concat(new Accessor(method).getPropertyName()); + if (annotation != null) { + return Arrays.asList(annotation.value()); + } + + return Collections.singletonList("$.".concat(new Accessor(method).getPropertyName())); } @RequiredArgsConstructor diff --git a/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java b/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java index 910312f6f..627508897 100755 --- a/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/web/JsonProjectingMethodInterceptorFactoryUnitTests.java @@ -136,6 +136,16 @@ public class JsonProjectingMethodInterceptorFactoryUnitTests { assertThat(customer.getNestedCities()).hasSize(2); } + @Test // DATACMNS-1144 + public void returnsNullForNonExistantValue() { + assertThat(customer.getName().getLastname()).isNull(); + } + + @Test // DATACMNS-1144 + public void triesMultipleDeclaredPathsIfNotAvailable() { + assertThat(customer.getName().getSomeName()).isEqualTo(customer.getName().getFirstname()); + } + interface Customer { String getFirstname(); @@ -187,8 +197,13 @@ public class JsonProjectingMethodInterceptorFactoryUnitTests { @JsonPath("$.firstname") String getFirstname(); + // Not available in the payload @JsonPath("$.lastname") String getLastname(); + + // First one not available in the payload + @JsonPath({ "$.lastname", "$.firstname" }) + String getSomeName(); } interface AnotherAddressProjection {