diff --git a/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java
new file mode 100644
index 00000000000..a5e644b9bdd
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2002-2012 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.http.converter;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import org.springframework.core.convert.ConversionService;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.util.Assert;
+
+/**
+ * An {@code HttpMessageConverter} that uses {@link StringHttpMessageConverter}
+ * for reading and writing content and a {@link ConversionService} for converting
+ * the String content to and from the target object type.
+ *
+ * By default, this converter supports the media type {@code text/plain} only.
+ * This can be overridden by setting the
+ * {@link #setSupportedMediaTypes supportedMediaTypes} property.
+ * Example of usage:
+ *
+ *
{
+
+ public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
+
+ private ConversionService conversionService;
+
+ private StringHttpMessageConverter stringHttpMessageConverter;
+
+
+ /**
+ * A constructor accepting a {@code ConversionService} to use to convert the
+ * (String) message body to/from the target class type.
+ * @param conversionService the conversion service
+ */
+ public ObjectToStringHttpMessageConverter(ConversionService conversionService) {
+ this(conversionService, DEFAULT_CHARSET);
+ }
+
+ public ObjectToStringHttpMessageConverter(ConversionService conversionService, Charset defaultCharset) {
+ super(new MediaType("text", "plain", defaultCharset));
+
+ Assert.notNull(conversionService, "conversionService is required");
+ this.conversionService = conversionService;
+ this.stringHttpMessageConverter = new StringHttpMessageConverter(defaultCharset);
+ }
+
+ /**
+ * Indicates whether the {@code Accept-Charset} should be written to any outgoing request.
+ * Default is {@code true}.
+ */
+ public void setWriteAcceptCharset(boolean writeAcceptCharset) {
+ this.stringHttpMessageConverter.setWriteAcceptCharset(writeAcceptCharset);
+ }
+
+ @Override
+ public boolean canRead(Class> clazz, MediaType mediaType) {
+ return this.conversionService.canConvert(String.class, clazz) && canRead(mediaType);
+ }
+
+ @Override
+ public boolean canWrite(Class> clazz, MediaType mediaType) {
+ return this.conversionService.canConvert(clazz, String.class) && canWrite(mediaType);
+ }
+
+ @Override
+ protected boolean supports(Class> clazz) {
+ // should not be called, since we override canRead/Write
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected Object readInternal(Class extends Object> clazz, HttpInputMessage inputMessage) throws IOException {
+ String value = this.stringHttpMessageConverter.readInternal(String.class, inputMessage);
+ return this.conversionService.convert(value, clazz);
+ }
+
+ @Override
+ protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException {
+ String s = this.conversionService.convert(obj, String.class);
+ this.stringHttpMessageConverter.writeInternal(s, outputMessage);
+ }
+
+ @Override
+ protected Long getContentLength(Object obj, MediaType contentType) {
+ String value = this.conversionService.convert(obj, String.class);
+ return this.stringHttpMessageConverter.getContentLength(value, contentType);
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java
index 3e59116b039..c037c1fcc93 100644
--- a/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java
+++ b/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java
@@ -76,14 +76,13 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter clazz) {
return String.class.equals(clazz);
}
@Override
- protected String readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException {
+ protected String readInternal(Class extends String> clazz, HttpInputMessage inputMessage) throws IOException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset));
}
@@ -126,5 +125,4 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverterDmitry Katsubo
+ * @author Rossen Stoyanchev
+ */
+public class ObjectToStringHttpMessageConverterTests {
+
+ private ObjectToStringHttpMessageConverter converter;
+
+ private MockHttpServletResponse servletResponse;
+
+ private ServletServerHttpResponse response;
+
+
+ @Before
+ public void setUp() {
+ ConversionService conversionService = new DefaultConversionService();
+ this.converter = new ObjectToStringHttpMessageConverter(conversionService);
+
+ this.servletResponse = new MockHttpServletResponse();
+ this.response = new ServletServerHttpResponse(this.servletResponse);
+ }
+
+ @Test
+ public void canRead() {
+ assertFalse(this.converter.canRead(Math.class, null));
+ assertFalse(this.converter.canRead(Resource.class, null));
+
+ assertTrue(this.converter.canRead(Locale.class, null));
+ assertTrue(this.converter.canRead(BigInteger.class, null));
+
+ assertFalse(this.converter.canRead(BigInteger.class, MediaType.TEXT_HTML));
+ assertFalse(this.converter.canRead(BigInteger.class, MediaType.TEXT_XML));
+ assertFalse(this.converter.canRead(BigInteger.class, MediaType.APPLICATION_XML));
+ }
+
+ @Test
+ public void canWrite() {
+ assertFalse(this.converter.canWrite(Math.class, null));
+ assertFalse(this.converter.canWrite(Resource.class, null));
+
+ assertTrue(this.converter.canWrite(Locale.class, null));
+ assertTrue(this.converter.canWrite(Double.class, null));
+
+ assertFalse(this.converter.canWrite(BigInteger.class, MediaType.TEXT_HTML));
+ assertFalse(this.converter.canWrite(BigInteger.class, MediaType.TEXT_XML));
+ assertFalse(this.converter.canWrite(BigInteger.class, MediaType.APPLICATION_XML));
+
+ assertTrue(this.converter.canWrite(BigInteger.class, MediaType.valueOf("text/*")));
+ }
+
+ @Test
+ public void defaultCharset() throws IOException {
+ this.converter.write(Integer.valueOf(5), null, response);
+
+ assertEquals("ISO-8859-1", servletResponse.getCharacterEncoding());
+ }
+
+ @Test
+ public void defaultCharsetModified() throws IOException {
+ Charset charset = Charset.forName("UTF-16");
+ ConversionService cs = new DefaultConversionService();
+ ObjectToStringHttpMessageConverter converter = new ObjectToStringHttpMessageConverter(cs, charset);
+ converter.write((byte) 31, null, this.response);
+
+ assertEquals("UTF-16", this.servletResponse.getCharacterEncoding());
+ }
+
+ @Test
+ public void writeAcceptCharset() throws IOException {
+ this.converter.write(new Date(), null, this.response);
+
+ assertNotNull(this.servletResponse.getHeader("Accept-Charset"));
+ }
+
+ @Test
+ public void writeAcceptCharsetTurnedOff() throws IOException {
+ this.converter.setWriteAcceptCharset(false);
+ this.converter.write(new Date(), null, this.response);
+
+ assertNull(this.servletResponse.getHeader("Accept-Charset"));
+ }
+
+ @Test
+ public void read() throws IOException {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+
+ request.setContentType(MediaType.TEXT_PLAIN_VALUE);
+
+ Short shortValue = Short.valueOf((short) 781);
+
+ request.setContent(shortValue.toString().getBytes(
+ StringHttpMessageConverter.DEFAULT_CHARSET));
+
+ assertEquals(shortValue, this.converter.read(Short.class, new ServletServerHttpRequest(request)));
+
+ Float floatValue = Float.valueOf(123);
+
+ request.setCharacterEncoding("UTF-16");
+ request.setContent(floatValue.toString().getBytes("UTF-16"));
+
+ assertEquals(floatValue, this.converter.read(Float.class, new ServletServerHttpRequest(request)));
+
+ Long longValue = Long.valueOf(55819182821331L);
+
+ request.setCharacterEncoding("UTF-8");
+ request.setContent(longValue.toString().getBytes("UTF-8"));
+
+ assertEquals(longValue, this.converter.read(Long.class, new ServletServerHttpRequest(request)));
+ }
+
+ @Test
+ public void write() throws IOException {
+ this.converter.write((byte) -8, null, this.response);
+
+ assertEquals("ISO-8859-1", this.servletResponse.getCharacterEncoding());
+ assertTrue(this.servletResponse.getContentType().startsWith(MediaType.TEXT_PLAIN_VALUE));
+ assertEquals(2, this.servletResponse.getContentLength());
+ assertArrayEquals(new byte[] { '-', '8' }, this.servletResponse.getContentAsByteArray());
+ }
+
+ @Test
+ public void writeUtf16() throws IOException {
+ MediaType contentType = new MediaType("text", "plain", Charset.forName("UTF-16"));
+ this.converter.write(Integer.valueOf(958), contentType, this.response);
+
+ assertEquals("UTF-16", this.servletResponse.getCharacterEncoding());
+ assertTrue(this.servletResponse.getContentType().startsWith(MediaType.TEXT_PLAIN_VALUE));
+ assertEquals(8, this.servletResponse.getContentLength());
+ // First two bytes: byte order mark
+ assertArrayEquals(new byte[] { -2, -1, 0, '9', 0, '5', 0, '8' }, this.servletResponse.getContentAsByteArray());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConversionServiceRequired() {
+ new ObjectToStringHttpMessageConverter(null);
+ }
+
+}