diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java index 9b2de2af20..85669247fd 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java @@ -258,7 +258,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener else { objectWriter = this.objectMapper.writer(); } - if (javaType != null) { + if (javaType != null && javaType.isContainerType()) { objectWriter = objectWriter.withType(javaType); } objectWriter.writeValue(generator, value); diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java index 589f751c71..6b1cfbbb77 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java @@ -315,8 +315,54 @@ public class MappingJackson2HttpMessageConverterTests { assertThat(result, not(containsString("\"withoutView\":\"without\""))); } + @Test // SPR-13318 + public void writeSubType() throws Exception { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + MyBean bean = new MyBean(); + bean.setString("Foo"); + bean.setNumber(42); - public static class MyBean { + this.converter.writeInternal(bean, MyInterface.class, outputMessage); + + String result = outputMessage.getBodyAsString(Charset.forName("UTF-8")); + assertTrue(result.contains("\"string\":\"Foo\"")); + assertTrue(result.contains("\"number\":42")); + } + + @Test // SPR-13318 + public void writeSubTypeList() throws Exception { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + List beans = new ArrayList(); + MyBean foo = new MyBean(); + foo.setString("Foo"); + foo.setNumber(42); + beans.add(foo); + MyBean bar = new MyBean(); + bar.setString("Bar"); + bar.setNumber(123); + beans.add(bar); + ParameterizedTypeReference> typeReference = + new ParameterizedTypeReference>() {}; + + this.converter.writeInternal(beans, typeReference.getType(), outputMessage); + + String result = outputMessage.getBodyAsString(Charset.forName("UTF-8")); + assertTrue(result.contains("\"string\":\"Foo\"")); + assertTrue(result.contains("\"number\":42")); + assertTrue(result.contains("\"string\":\"Bar\"")); + assertTrue(result.contains("\"number\":123")); + } + + + interface MyInterface { + + String getString(); + + void setString(String string); + } + + + public static class MyBean implements MyInterface { private String string; diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java index 8dcea7b795..c4861a1cca 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java @@ -24,6 +24,7 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -543,7 +544,7 @@ public class RequestResponseBodyMethodProcessorTests { @Test // SPR-12811 public void jacksonTypeInfoList() throws Exception { - Method method = JacksonController.class.getMethod("handleList"); + Method method = JacksonController.class.getMethod("handleTypeInfoList"); HandlerMethod handlerMethod = new HandlerMethod(new JacksonController(), method); MethodParameter methodReturnType = handlerMethod.getReturnType(); @@ -551,7 +552,7 @@ public class RequestResponseBodyMethodProcessorTests { converters.add(new MappingJackson2HttpMessageConverter()); RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters); - Object returnValue = new JacksonController().handleList(); + Object returnValue = new JacksonController().handleTypeInfoList(); processor.handleReturnValue(returnValue, methodReturnType, this.mavContainer, this.webRequest); String content = this.servletResponse.getContentAsString(); @@ -559,6 +560,44 @@ public class RequestResponseBodyMethodProcessorTests { assertTrue(content.contains("\"type\":\"bar\"")); } + @Test // SPR-13318 + public void jacksonSubType() throws Exception { + Method method = JacksonController.class.getMethod("handleSubType"); + HandlerMethod handlerMethod = new HandlerMethod(new JacksonController(), method); + MethodParameter methodReturnType = handlerMethod.getReturnType(); + + List> converters = new ArrayList>(); + converters.add(new MappingJackson2HttpMessageConverter()); + RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters); + + Object returnValue = new JacksonController().handleSubType(); + processor.handleReturnValue(returnValue, methodReturnType, this.mavContainer, this.webRequest); + + String content = this.servletResponse.getContentAsString(); + assertTrue(content.contains("\"id\":123")); + assertTrue(content.contains("\"name\":\"foo\"")); + } + + @Test // SPR-13318 + public void jacksonSubTypeList() throws Exception { + Method method = JacksonController.class.getMethod("handleSubTypeList"); + HandlerMethod handlerMethod = new HandlerMethod(new JacksonController(), method); + MethodParameter methodReturnType = handlerMethod.getReturnType(); + + List> converters = new ArrayList>(); + converters.add(new MappingJackson2HttpMessageConverter()); + RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters); + + Object returnValue = new JacksonController().handleSubTypeList(); + processor.handleReturnValue(returnValue, methodReturnType, this.mavContainer, this.webRequest); + + String content = this.servletResponse.getContentAsString(); + assertTrue(content.contains("\"id\":123")); + assertTrue(content.contains("\"name\":\"foo\"")); + assertTrue(content.contains("\"id\":456")); + assertTrue(content.contains("\"name\":\"bar\"")); + } + String handle( @RequestBody List list, @@ -774,12 +813,33 @@ public class RequestResponseBodyMethodProcessorTests { @RequestMapping @ResponseBody - public List handleList() { + public List handleTypeInfoList() { List list = new ArrayList<>(); list.add(new Foo("foo")); list.add(new Bar("bar")); return list; } + + @RequestMapping + @ResponseBody + public Identifiable handleSubType() { + SimpleBean foo = new SimpleBean(); + foo.setId(123L); + foo.setName("foo"); + return foo; + } + + @RequestMapping + @ResponseBody + public List handleSubTypeList() { + SimpleBean foo = new SimpleBean(); + foo.setId(123L); + foo.setName("foo"); + SimpleBean bar = new SimpleBean(); + bar.setId(456L); + bar.setName("bar"); + return Arrays.asList(foo, bar); + } } private static class EmptyRequestBodyAdvice implements RequestBodyAdvice {