From 212daa19954ec639e493554ade50149995f4e802 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 23 Mar 2010 11:15:13 +0000 Subject: [PATCH] SPR-7002 - RestTemplate fails to convert properly for Generic Type Container with MappingJacksonHttpMessageConverter --- .../MappingJacksonHttpMessageConverter.java | 29 +++++++++++++--- ...ppingJacksonHttpMessageConverterTests.java | 33 ++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java b/org.springframework.web/src/main/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java index 00635716e07..deafebd8e53 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java +++ b/org.springframework.web/src/main/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java @@ -86,13 +86,34 @@ public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConve this.prefixJson = prefixJson; } - @Override public boolean canRead(Class clazz, MediaType mediaType) { - JavaType javaType = TypeFactory.fromClass(clazz); + JavaType javaType = getJavaType(clazz); return this.objectMapper.canDeserialize(javaType) && canRead(mediaType); } + /** + * Returns the Jackson {@link JavaType} for the specific class. + * + *

Default implementation returns {@link TypeFactory#type(java.lang.reflect.Type)}, but this can be overridden + * in subclasses, to allow for custom generic collection handling. For instance: + *

+	 * protected JavaType getJavaType(Class<?> clazz) {
+	 *   if (List.class.isAssignableFrom(clazz)) {
+	 *     return TypeFactory.collectionType(ArrayList.class, MyBean.class);
+	 *   } else {
+	 *     return super.getJavaType(clazz);
+	 *   }
+	 * }
+	 * 
+ * + * @param clazz the class to return the java type for + * @return the java type + */ + protected JavaType getJavaType(Class clazz) { + return TypeFactory.type(clazz); + } + @Override public boolean canWrite(Class clazz, MediaType mediaType) { return this.objectMapper.canSerialize(clazz) && canWrite(mediaType); @@ -107,8 +128,8 @@ public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConve @Override protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { - - return this.objectMapper.readValue(inputMessage.getBody(), clazz); + JavaType javaType = getJavaType(clazz); + return this.objectMapper.readValue(inputMessage.getBody(), javaType); } @Override diff --git a/org.springframework.web/src/test/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverterTests.java b/org.springframework.web/src/test/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverterTests.java index 32fb7fd3b16..a79ec58b66f 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverterTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverterTests.java @@ -23,6 +23,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.codehaus.jackson.map.type.TypeFactory; +import org.codehaus.jackson.type.JavaType; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; @@ -56,7 +58,6 @@ public class MappingJacksonHttpMessageConverterTests { } @Test - @SuppressWarnings("unchecked") public void readTyped() throws IOException { String body = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; @@ -71,6 +72,36 @@ public class MappingJacksonHttpMessageConverterTests { assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); } + @Test + @SuppressWarnings("unchecked") + public void readGenerics() throws IOException { + converter = new MappingJacksonHttpMessageConverter() { + @Override + protected JavaType getJavaType(Class clazz) { + if (List.class.isAssignableFrom(clazz)) { + return TypeFactory.collectionType(ArrayList.class, MyBean.class); + } + else { + return super.getJavaType(clazz); + } + } + }; + String body = + "[{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + + List results = (List) converter.read(List.class, inputMessage); + assertEquals(1, results.size()); + MyBean result = results.get(0); + assertEquals("Foo", result.getString()); + assertEquals(42, result.getNumber()); + assertEquals(42F, result.getFraction(), 0F); + assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); + assertTrue(result.isBool()); + assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); + } + @Test @SuppressWarnings("unchecked") public void readUntyped() throws IOException {