Skip to content

Commit 3a4e5d5

Browse files
committed
Fix ParameterizedType + contextClass support in Jackson converter
Issue: SPR-14470
1 parent b52b56c commit 3a4e5d5

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;
@@ -312,11 +313,37 @@ protected void writeSuffix(JsonGenerator generator, Object object) throws IOExce
312313
*/
313314
protected JavaType getJavaType(Type type, Class<?> contextClass) {
314315
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
315-
if (type instanceof TypeVariable && contextClass != null) {
316-
ResolvableType resolvedType = resolveVariable(
317-
(TypeVariable<?>) type, ResolvableType.forClass(contextClass));
318-
if (resolvedType != ResolvableType.NONE) {
319-
return typeFactory.constructType(resolvedType.resolve());
316+
if (contextClass != null) {
317+
ResolvableType resolvedType = ResolvableType.forType(type);
318+
if (type instanceof TypeVariable) {
319+
ResolvableType resolvedTypeVariable = resolveVariable(
320+
(TypeVariable<?>) type, ResolvableType.forClass(contextClass));
321+
if (resolvedTypeVariable != ResolvableType.NONE) {
322+
return typeFactory.constructType(resolvedTypeVariable.resolve());
323+
}
324+
}
325+
else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) {
326+
ParameterizedType parameterizedType = (ParameterizedType) type;
327+
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
328+
Type[] typeArguments = parameterizedType.getActualTypeArguments();
329+
for (int i = 0; i < typeArguments.length; i++) {
330+
Type typeArgument = typeArguments[i];
331+
if (typeArgument instanceof TypeVariable) {
332+
ResolvableType resolvedTypeArgument = resolveVariable(
333+
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
334+
if (resolvedTypeArgument != ResolvableType.NONE) {
335+
generics[i] = resolvedTypeArgument.resolve();
336+
}
337+
else {
338+
generics[i] = ResolvableType.forType(typeArgument).resolve();
339+
}
340+
}
341+
else {
342+
generics[i] = ResolvableType.forType(typeArgument).resolve();
343+
}
344+
}
345+
return typeFactory.constructType(ResolvableType.
346+
forClassWithGenerics(resolvedType.getRawClass(), generics).getType());
320347
}
321348
}
322349
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 MySimpleParameterizedControllerWithList(), 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)