diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java index a12caf7ba6..35f8aa16c2 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java @@ -37,7 +37,9 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; @@ -207,7 +209,7 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { @Override @Nullable protected Object convertFromInternal(Message message, Class targetClass, @Nullable Object conversionHint) { - JavaType javaType = this.objectMapper.constructType(targetClass); + JavaType javaType = getJavaType(targetClass, conversionHint); Object payload = message.getPayload(); Class view = getSerializationView(conversionHint); // Note: in the view case, calling withType instead of forType for compatibility with Jackson <2.5 @@ -234,6 +236,18 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { } } + private JavaType getJavaType(Class targetClass, @Nullable Object conversionHint) { + if (conversionHint instanceof MethodParameter) { + MethodParameter param = (MethodParameter) conversionHint; + param = param.nestedIfOptional(); + Type genericParameterType = param.getNestedGenericParameterType(); + Class contextClass = param.getContainingClass(); + Type type = GenericTypeResolver.resolveType(genericParameterType, contextClass); + return this.objectMapper.getTypeFactory().constructType(type); + } + return this.objectMapper.constructType(targetClass); + } + @Override @Nullable protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, diff --git a/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java b/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java index 8986f3dcab..e882dad7dd 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonView; @@ -33,8 +34,16 @@ import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.MimeType; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * Test fixture for {@link org.springframework.messaging.converter.MappingJackson2MessageConverter}. @@ -127,6 +136,20 @@ public class MappingJackson2MessageConverterTests { assertEquals("string", myBean.getString()); } + @Test // SPR-16252 + public void fromMessageToList() throws Exception { + MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); + String payload = "[1, 2, 3, 4, 5, 6, 7, 8, 9]"; + Message message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build(); + + Method method = getClass().getDeclaredMethod("handleList", List.class); + MethodParameter param = new MethodParameter(method, 0); + Object actual = converter.fromMessage(message, List.class, param); + + assertNotNull(actual); + assertEquals(Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L), actual); + } + @Test public void toMessage() throws Exception { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); @@ -217,6 +240,8 @@ public class MappingJackson2MessageConverterTests { public void jsonViewPayload(@JsonView(MyJacksonView2.class) JacksonViewBean payload) { } + void handleList(List payload) {} + public static class MyBean {