Browse Source

MappingJackson2MessageConverter uses generic type

This is a backport of the #583201 minus use of GenericTypeResolver
which in 5.0 has been refactored to provide a getJavaType method.

Issue: SPR-16252
pull/1638/head
Rossen Stoyanchev 8 years ago
parent
commit
6dbc828aa1
  1. 81
      spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java
  2. 21
      spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java

81
spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java

@ -20,7 +20,9 @@ import java.io.ByteArrayOutputStream; @@ -20,7 +20,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
@ -35,8 +37,10 @@ import com.fasterxml.jackson.databind.JsonMappingException; @@ -35,8 +37,10 @@ import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert;
@ -202,7 +206,7 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { @@ -202,7 +206,7 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
@Override
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, 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
@ -229,6 +233,81 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { @@ -229,6 +233,81 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
}
}
private JavaType getJavaType(Class<?> targetClass, Object conversionHint) {
if (conversionHint instanceof MethodParameter) {
MethodParameter param = (MethodParameter) conversionHint;
param = param.nestedIfOptional();
Type genericParameterType = param.getNestedGenericParameterType();
Class<?> contextClass = param.getContainingClass();
Type type = getJavaType(genericParameterType, contextClass);
return this.objectMapper.getTypeFactory().constructType(type);
}
return this.objectMapper.constructType(targetClass);
}
private JavaType getJavaType(Type type, Class<?> contextClass) {
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
if (contextClass != null) {
ResolvableType resolvedType = ResolvableType.forType(type);
if (type instanceof TypeVariable) {
ResolvableType resolvedTypeVariable = resolveVariable(
(TypeVariable<?>) type, ResolvableType.forClass(contextClass));
if (resolvedTypeVariable != ResolvableType.NONE) {
return typeFactory.constructType(resolvedTypeVariable.resolve());
}
}
else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
Type[] typeArguments = parameterizedType.getActualTypeArguments();
for (int i = 0; i < typeArguments.length; i++) {
Type typeArgument = typeArguments[i];
if (typeArgument instanceof TypeVariable) {
ResolvableType resolvedTypeArgument = resolveVariable(
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
if (resolvedTypeArgument != ResolvableType.NONE) {
generics[i] = resolvedTypeArgument.resolve();
}
else {
generics[i] = ResolvableType.forType(typeArgument).resolve();
}
}
else {
generics[i] = ResolvableType.forType(typeArgument).resolve();
}
}
return typeFactory.constructType(ResolvableType.
forClassWithGenerics(resolvedType.getRawClass(), generics).getType());
}
}
return typeFactory.constructType(type);
}
private ResolvableType resolveVariable(TypeVariable<?> typeVariable, ResolvableType contextType) {
ResolvableType resolvedType;
if (contextType.hasGenerics()) {
resolvedType = ResolvableType.forType(typeVariable, contextType);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
ResolvableType superType = contextType.getSuperType();
if (superType != ResolvableType.NONE) {
resolvedType = resolveVariable(typeVariable, superType);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
for (ResolvableType ifc : contextType.getInterfaces()) {
resolvedType = resolveVariable(typeVariable, ifc);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
return ResolvableType.NONE;
}
@Override
protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) {
try {

21
spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java

@ -19,8 +19,10 @@ package org.springframework.messaging.converter; @@ -19,8 +19,10 @@ package org.springframework.messaging.converter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
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;
@ -120,6 +122,20 @@ public class MappingJackson2MessageConverterTests { @@ -120,6 +122,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();
@ -209,8 +225,9 @@ public class MappingJackson2MessageConverterTests { @@ -209,8 +225,9 @@ public class MappingJackson2MessageConverterTests {
return bean;
}
public void jsonViewPayload(@JsonView(MyJacksonView2.class) JacksonViewBean payload) {
}
public void jsonViewPayload(@JsonView(MyJacksonView2.class) JacksonViewBean payload) {}
void handleList(List<Long> payload) {}
public static class MyBean {

Loading…
Cancel
Save