diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractGenericHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractGenericHttpMessageConverter.java index ea321160c1a..df17625bdf4 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractGenericHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractGenericHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,21 +87,16 @@ public abstract class AbstractGenericHttpMessageConverter extends AbstractHtt if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; - streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() { + streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() { @Override - public void writeTo(final OutputStream outputStream) throws IOException { - writeInternal(t, type, new HttpOutputMessage() { - @Override - public OutputStream getBody() throws IOException { - return outputStream; - } - @Override - public HttpHeaders getHeaders() { - return headers; - } - }); + public OutputStream getBody() { + return outputStream; } - }); + @Override + public HttpHeaders getHeaders() { + return headers; + } + })); } else { writeInternal(t, type, outputMessage); diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java index ecf782f0746..025414ffdfc 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.http.converter.json; import java.io.Reader; import java.io.Writer; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import com.google.gson.Gson; @@ -34,7 +35,7 @@ import org.springframework.util.Assert; * By default, it supports {@code application/json} and {@code application/*+json} with * {@code UTF-8} character set. * - *

Tested against Gson 2.6; compatible with Gson 2.0 and higher. + *

Tested against Gson 2.8; compatible with Gson 2.0 and higher. * * @author Roy Clarkson * @author Juergen Hoeller @@ -93,7 +94,12 @@ public class GsonHttpMessageConverter extends AbstractJsonHttpMessageConverter { @Override protected void writeInternal(Object o, @Nullable Type type, Writer writer) throws Exception { - if (type != null) { + // In Gson, toJson with a type argument will exclusively use that given type, + // ignoring the actual type of the object... which might be more specific, + // e.g. a subclass of the specified type which includes additional fields. + // As a consequence, we're only passing in parameterized type declarations + // which might contain extra generics that the object instance doesn't retain. + if (type instanceof ParameterizedType) { getGson().toJson(o, type, writer); } else { diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/JsonbHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/JsonbHttpMessageConverter.java index 3d9e5101988..a4ed6a751e3 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/JsonbHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/JsonbHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.http.converter.json; import java.io.Reader; import java.io.Writer; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; @@ -100,7 +101,7 @@ public class JsonbHttpMessageConverter extends AbstractJsonHttpMessageConverter @Override protected void writeInternal(Object o, @Nullable Type type, Writer writer) throws Exception { - if (type != null) { + if (type instanceof ParameterizedType) { getJsonb().toJson(o, type, writer); } else { diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/GsonHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/GsonHttpMessageConverterTests.java index 4142fe60b65..88efb44ca93 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/GsonHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/GsonHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.MediaType; @@ -40,6 +41,7 @@ import static org.junit.Assert.*; * Gson 2.x converter tests. * * @author Roy Clarkson + * @author Juergen Hoeller */ public class GsonHttpMessageConverterTests { @@ -129,6 +131,29 @@ public class GsonHttpMessageConverterTests { outputMessage.getHeaders().getContentType()); } + @Test + public void writeWithBaseType() throws IOException { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + MyBean body = new MyBean(); + body.setString("Foo"); + body.setNumber(42); + body.setFraction(42F); + body.setArray(new String[] {"Foo", "Bar"}); + body.setBool(true); + body.setBytes(new byte[] {0x1, 0x2}); + this.converter.write(body, MyBase.class, null, outputMessage); + Charset utf8 = StandardCharsets.UTF_8; + String result = outputMessage.getBodyAsString(utf8); + assertTrue(result.contains("\"string\":\"Foo\"")); + assertTrue(result.contains("\"number\":42")); + assertTrue(result.contains("fraction\":42.0")); + assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]")); + assertTrue(result.contains("\"bool\":true")); + assertTrue(result.contains("\"bytes\":[1,2]")); + assertEquals("Invalid content-type", new MediaType("application", "json", utf8), + outputMessage.getHeaders().getContentType()); + } + @Test public void writeUTF16() throws IOException { MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE); @@ -149,7 +174,7 @@ public class GsonHttpMessageConverterTests { @Test @SuppressWarnings("unchecked") - public void readGenerics() throws Exception { + public void readAndWriteGenerics() throws Exception { Field beansList = ListHolder.class.getField("listField"); String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," + @@ -164,14 +189,18 @@ public class GsonHttpMessageConverterTests { assertEquals("Foo", result.getString()); assertEquals(42, result.getNumber()); assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[] { "Foo", "Bar" }, result.getArray()); + assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray()); assertTrue(result.isBool()); - assertArrayEquals(new byte[] { 0x1, 0x2 }, result.getBytes()); + assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, genericType, new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); } @Test @SuppressWarnings("unchecked") - public void readParameterizedType() throws Exception { + public void readAndWriteParameterizedType() throws Exception { ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() { }; @@ -186,13 +215,43 @@ public class GsonHttpMessageConverterTests { assertEquals("Foo", result.getString()); assertEquals(42, result.getNumber()); assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[] { "Foo", "Bar" }, result.getArray()); + assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray()); assertTrue(result.isBool()); assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, beansList.getType(), new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); } @Test - public void prefixJson() throws Exception { + @SuppressWarnings("unchecked") + public void writeParameterizedBaseType() throws Exception { + ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() {}; + ParameterizedTypeReference> baseList = new ParameterizedTypeReference>() {}; + + String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," + + "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8)); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + + List results = (List) converter.read(beansList.getType(), null, 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()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, baseList.getType(), new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); + } + + @Test + public void prefixJson() throws IOException { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); this.converter.setPrefixJson(true); this.converter.writeInternal("foo", null, outputMessage); @@ -200,7 +259,7 @@ public class GsonHttpMessageConverterTests { } @Test - public void prefixJsonCustom() throws Exception { + public void prefixJsonCustom() throws IOException { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); this.converter.setJsonPrefix(")))"); this.converter.writeInternal("foo", null, outputMessage); @@ -208,10 +267,22 @@ public class GsonHttpMessageConverterTests { } - public static class MyBean { + public static class MyBase { private String string; + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + } + + + public static class MyBean extends MyBase { + private int number; private float fraction; @@ -222,30 +293,6 @@ public class GsonHttpMessageConverterTests { private byte[] bytes; - public byte[] getBytes() { - return bytes; - } - - public void setBytes(byte[] bytes) { - this.bytes = bytes; - } - - public boolean isBool() { - return bool; - } - - public void setBool(boolean bool) { - this.bool = bool; - } - - public String getString() { - return string; - } - - public void setString(String string) { - this.string = string; - } - public int getNumber() { return number; } @@ -269,6 +316,22 @@ public class GsonHttpMessageConverterTests { public void setArray(String[] array) { this.array = array; } + + public boolean isBool() { + return bool; + } + + public void setBool(boolean bool) { + this.bool = bool; + } + + public byte[] getBytes() { + return bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } } diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/JsonbHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/JsonbHttpMessageConverterTests.java index 90b06eb28ee..33dd96c4501 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/JsonbHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/JsonbHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.springframework.http.converter.json; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -25,6 +27,7 @@ import java.util.List; import java.util.Map; import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.MediaType; @@ -128,6 +131,29 @@ public class JsonbHttpMessageConverterTests { outputMessage.getHeaders().getContentType()); } + @Test + public void writeWithBaseType() throws IOException { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + MyBean body = new MyBean(); + body.setString("Foo"); + body.setNumber(42); + body.setFraction(42F); + body.setArray(new String[] {"Foo", "Bar"}); + body.setBool(true); + body.setBytes(new byte[] {0x1, 0x2}); + this.converter.write(body, MyBase.class, null, outputMessage); + Charset utf8 = StandardCharsets.UTF_8; + String result = outputMessage.getBodyAsString(utf8); + assertTrue(result.contains("\"string\":\"Foo\"")); + assertTrue(result.contains("\"number\":42")); + assertTrue(result.contains("fraction\":42.0")); + assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]")); + assertTrue(result.contains("\"bool\":true")); + assertTrue(result.contains("\"bytes\":[1,2]")); + assertEquals("Invalid content-type", new MediaType("application", "json", utf8), + outputMessage.getHeaders().getContentType()); + } + @Test public void writeUTF16() throws IOException { MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE); @@ -148,9 +174,34 @@ public class JsonbHttpMessageConverterTests { @Test @SuppressWarnings("unchecked") - public void readParameterizedType() throws IOException { - ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() { - }; + public void readAndWriteGenerics() throws Exception { + Field beansList = ListHolder.class.getField("listField"); + + String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," + + "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8)); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + + Type genericType = beansList.getGenericType(); + List results = (List) converter.read(genericType, MyBeanListHolder.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()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, genericType, new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); + } + + @Test + @SuppressWarnings("unchecked") + public void readAndWriteParameterizedType() throws Exception { + ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() {}; String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," + "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]"; @@ -163,16 +214,74 @@ public class JsonbHttpMessageConverterTests { assertEquals("Foo", result.getString()); assertEquals(42, result.getNumber()); assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[] { "Foo", "Bar" }, result.getArray()); + assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray()); assertTrue(result.isBool()); assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, beansList.getType(), new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); + } + + @Test + @SuppressWarnings("unchecked") + public void writeParameterizedBaseType() throws Exception { + ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() {}; + ParameterizedTypeReference> baseList = new ParameterizedTypeReference>() {}; + + String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," + + "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8)); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + + List results = (List) converter.read(beansList.getType(), null, 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()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, baseList.getType(), new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); + } + + @Test + public void prefixJson() throws IOException { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + this.converter.setPrefixJson(true); + this.converter.writeInternal("foo", null, outputMessage); + assertEquals(")]}', foo", outputMessage.getBodyAsString(StandardCharsets.UTF_8)); + } + + @Test + public void prefixJsonCustom() throws IOException { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + this.converter.setJsonPrefix(")))"); + this.converter.writeInternal("foo", null, outputMessage); + assertEquals(")))foo", outputMessage.getBodyAsString(StandardCharsets.UTF_8)); } - public static class MyBean { + public static class MyBase { private String string; + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + } + + + public static class MyBean extends MyBase { + private int number; private float fraction; @@ -183,30 +292,6 @@ public class JsonbHttpMessageConverterTests { private byte[] bytes; - public byte[] getBytes() { - return bytes; - } - - public void setBytes(byte[] bytes) { - this.bytes = bytes; - } - - public boolean isBool() { - return bool; - } - - public void setBool(boolean bool) { - this.bool = bool; - } - - public String getString() { - return string; - } - - public void setString(String string) { - this.string = string; - } - public int getNumber() { return number; } @@ -230,6 +315,32 @@ public class JsonbHttpMessageConverterTests { public void setArray(String[] array) { this.array = array; } + + public boolean isBool() { + return bool; + } + + public void setBool(boolean bool) { + this.bool = bool; + } + + public byte[] getBytes() { + return bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + } + + + public static class ListHolder { + + public List listField; + } + + + public static class MyBeanListHolder extends ListHolder { } } 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 4763289b343..da292caadba 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.MediaType; @@ -41,21 +42,15 @@ import org.springframework.http.converter.HttpMessageConversionException; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.lang.Nullable; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.endsWith; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.startsWith; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; /** * Jackson 2.x converter tests. * * @author Rossen Stoyanchev * @author Sebastien Deleuze + * @author Juergen Hoeller */ public class MappingJackson2HttpMessageConverterTests { @@ -97,9 +92,9 @@ public class MappingJackson2HttpMessageConverterTests { assertEquals("Foo", result.getString()); assertEquals(42, result.getNumber()); assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); + assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray()); assertTrue(result.isBool()); - assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); + assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes()); } @Test @@ -133,9 +128,9 @@ public class MappingJackson2HttpMessageConverterTests { body.setString("Foo"); body.setNumber(42); body.setFraction(42F); - body.setArray(new String[]{"Foo", "Bar"}); + body.setArray(new String[] {"Foo", "Bar"}); body.setBool(true); - body.setBytes(new byte[]{0x1, 0x2}); + body.setBytes(new byte[] {0x1, 0x2}); converter.write(body, null, outputMessage); String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8); assertTrue(result.contains("\"string\":\"Foo\"")); @@ -148,6 +143,28 @@ public class MappingJackson2HttpMessageConverterTests { outputMessage.getHeaders().getContentType()); } + @Test + public void writeWithBaseType() throws IOException { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + MyBean body = new MyBean(); + body.setString("Foo"); + body.setNumber(42); + body.setFraction(42F); + body.setArray(new String[] {"Foo", "Bar"}); + body.setBool(true); + body.setBytes(new byte[] {0x1, 0x2}); + converter.write(body, MyBase.class, null, outputMessage); + String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8); + assertTrue(result.contains("\"string\":\"Foo\"")); + assertTrue(result.contains("\"number\":42")); + assertTrue(result.contains("fraction\":42.0")); + assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]")); + assertTrue(result.contains("\"bool\":true")); + assertTrue(result.contains("\"bytes\":\"AQI=\"")); + assertEquals("Invalid content-type", new MediaType("application", "json", StandardCharsets.UTF_8), + outputMessage.getHeaders().getContentType()); + } + @Test public void writeUTF16() throws IOException { MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE); @@ -177,7 +194,7 @@ public class MappingJackson2HttpMessageConverterTests { @Test @SuppressWarnings("unchecked") - public void readGenerics() throws IOException { + public void readAndWriteGenerics() throws Exception { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter() { @Override protected JavaType getJavaType(Type type, @Nullable Class contextClass) { @@ -205,14 +222,18 @@ public class MappingJackson2HttpMessageConverterTests { assertEquals("Foo", result.getString()); assertEquals(42, result.getNumber()); assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); + assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray()); assertTrue(result.isBool()); - assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); + assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); } @Test @SuppressWarnings("unchecked") - public void readParameterizedType() throws IOException { + public void readAndWriteParameterizedType() throws Exception { ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() {}; String body = "[{" + @@ -232,11 +253,46 @@ public class MappingJackson2HttpMessageConverterTests { assertEquals("Foo", result.getString()); assertEquals(42, result.getNumber()); assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); + assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray()); assertTrue(result.isBool()); - assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); + assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, beansList.getType(), new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); } + @Test + @SuppressWarnings("unchecked") + public void writeParameterizedBaseType() throws Exception { + ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() {}; + ParameterizedTypeReference> baseList = new ParameterizedTypeReference>() {}; + + 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")); + + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + List results = (List) converter.read(beansList.getType(), null, 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()); + + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + converter.write(results, baseList.getType(), new MediaType("application", "json"), outputMessage); + JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true); + } @Test public void prettyPrint() throws Exception { @@ -433,10 +489,22 @@ public class MappingJackson2HttpMessageConverterTests { } - public static class MyBean implements MyInterface { + public static class MyBase implements MyInterface{ private String string; + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + } + + + public static class MyBean extends MyBase { + private int number; private float fraction; @@ -447,30 +515,6 @@ public class MappingJackson2HttpMessageConverterTests { private byte[] bytes; - public byte[] getBytes() { - return bytes; - } - - public void setBytes(byte[] bytes) { - this.bytes = bytes; - } - - public boolean isBool() { - return bool; - } - - public void setBool(boolean bool) { - this.bool = bool; - } - - public String getString() { - return string; - } - - public void setString(String string) { - this.string = string; - } - public int getNumber() { return number; } @@ -494,6 +538,22 @@ public class MappingJackson2HttpMessageConverterTests { public void setArray(String[] array) { this.array = array; } + + public boolean isBool() { + return bool; + } + + public void setBool(boolean bool) { + this.bool = bool; + } + + public byte[] getBytes() { + return bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } }