Skip to content

Commit 940bdd8

Browse files
committed
Fix ParameterizedType + contextClass support in Jackson converter
Issue: SPR-14470
1 parent df55633 commit 940bdd8

File tree

2 files changed

+66
-5
lines changed

2 files changed

+66
-5
lines changed

spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.http.converter.json;
1818

1919
import java.io.IOException;
20+
import java.lang.reflect.ParameterizedType;
2021
import java.lang.reflect.Type;
2122
import java.lang.reflect.TypeVariable;
2223
import java.nio.charset.Charset;
@@ -311,11 +312,37 @@ protected void writeSuffix(JsonGenerator generator, Object object) throws IOExce
311312
*/
312313
protected JavaType getJavaType(Type type, Class<?> contextClass) {
313314
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
314-
if (type instanceof TypeVariable && contextClass != null) {
315-
ResolvableType resolvedType = resolveVariable(
316-
(TypeVariable<?>) type, ResolvableType.forClass(contextClass));
317-
if (resolvedType != ResolvableType.NONE) {
318-
return typeFactory.constructType(resolvedType.resolve());
315+
if (contextClass != null) {
316+
ResolvableType resolvedType = ResolvableType.forType(type);
317+
if (type instanceof TypeVariable) {
318+
ResolvableType resolvedTypeVariable = resolveVariable(
319+
(TypeVariable<?>) type, ResolvableType.forClass(contextClass));
320+
if (resolvedTypeVariable != ResolvableType.NONE) {
321+
return typeFactory.constructType(resolvedTypeVariable.resolve());
322+
}
323+
}
324+
else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) {
325+
ParameterizedType parameterizedType = (ParameterizedType) type;
326+
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
327+
Type[] typeArguments = parameterizedType.getActualTypeArguments();
328+
for (int i = 0; i < typeArguments.length; i++) {
329+
Type typeArgument = typeArguments[i];
330+
if (typeArgument instanceof TypeVariable) {
331+
ResolvableType resolvedTypeArgument = resolveVariable(
332+
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
333+
if (resolvedTypeArgument != ResolvableType.NONE) {
334+
generics[i] = resolvedTypeArgument.resolve();
335+
}
336+
else {
337+
generics[i] = ResolvableType.forType(typeArgument).resolve();
338+
}
339+
}
340+
else {
341+
generics[i] = ResolvableType.forType(typeArgument).resolve();
342+
}
343+
}
344+
return typeFactory.constructType(ResolvableType.
345+
forClassWithGenerics(resolvedType.getRawClass(), generics).getType());
319346
}
320347
}
321348
return typeFactory.constructType(type);

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,29 @@ public void resolveArgumentTypeVariable() throws Exception {
240240
assertEquals("Jad", result.getName());
241241
}
242242

243+
@Test // SPR-14470
244+
public void resolveParameterizedWithTypeVariableArgument() throws Exception {
245+
Method method = MyParameterizedControllerWithList.class.getMethod("handleDto", List.class);
246+
HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedController(), method);
247+
MethodParameter methodParam = handlerMethod.getMethodParameters()[0];
248+
249+
String content = "[{\"name\" : \"Jad\"}, {\"name\" : \"Robert\"}]";
250+
this.servletRequest.setContent(content.getBytes("UTF-8"));
251+
this.servletRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
252+
253+
List<HttpMessageConverter<?>> converters = new ArrayList<>();
254+
converters.add(new MappingJackson2HttpMessageConverter());
255+
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
256+
257+
@SuppressWarnings("unchecked")
258+
List<SimpleBean> result = (List<SimpleBean>)
259+
processor.resolveArgument(methodParam, container, request, factory);
260+
261+
assertNotNull(result);
262+
assertEquals("Jad", result.get(0).getName());
263+
assertEquals("Robert", result.get(1).getName());
264+
}
265+
243266
@Test // SPR-11225
244267
public void resolveArgumentTypeVariableWithNonGenericConverter() throws Exception {
245268
Method method = MyParameterizedController.class.getMethod("handleDto", Identifiable.class);
@@ -725,6 +748,17 @@ private interface Identifiable extends Serializable {
725748
void setId(Long id);
726749
}
727750

751+
@SuppressWarnings("unused")
752+
private static abstract class MyParameterizedControllerWithList<DTO extends Identifiable> {
753+
754+
public void handleDto(@RequestBody List<DTO> dto) {
755+
}
756+
}
757+
758+
@SuppressWarnings("unused")
759+
private static class MySimpleParameterizedControllerWithList extends MyParameterizedControllerWithList<SimpleBean> {
760+
}
761+
728762

729763
@SuppressWarnings({ "serial" })
730764
private static class SimpleBean implements Identifiable {

0 commit comments

Comments
 (0)