提交 01ce468f 编写于 作者: A Arjen Poutsma

SPR-6386 - MappingJacksonHttpMessageConverter ignores supported media types property

上级 dc0613f4
...@@ -20,9 +20,14 @@ import java.io.IOException; ...@@ -20,9 +20,14 @@ import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpOutputMessage;
...@@ -39,24 +44,24 @@ import org.springframework.util.Assert; ...@@ -39,24 +44,24 @@ import org.springframework.util.Assert;
* <p>This converter can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances. * <p>This converter can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances.
* *
* <p>By default, this converter supports {@code application/json}. This can be overridden by setting the {@link * <p>By default, this converter supports {@code application/json}. This can be overridden by setting the {@link
* #setSupportedMediaTypes(List) supportedMediaTypes} property, and overriding the {@link #getContentType(Object)} * #setSupportedMediaTypes(List) supportedMediaTypes} property.
* method. * method.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @see org.springframework.web.servlet.view.json.BindingJacksonJsonView * @see org.springframework.web.servlet.view.json.BindingJacksonJsonView
* @since 3.0 * @since 3.0
*/ */
public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageConverter<T> { public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private ObjectMapper objectMapper = new ObjectMapper(); public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private JsonEncoding encoding = JsonEncoding.UTF8; private ObjectMapper objectMapper = new ObjectMapper();
private boolean prefixJson = false; private boolean prefixJson = false;
/** Construct a new {@code BindingJacksonHttpMessageConverter}, */ /** Construct a new {@code BindingJacksonHttpMessageConverter}, */
public MappingJacksonHttpMessageConverter() { public MappingJacksonHttpMessageConverter() {
super(new MediaType("application", "json")); super(new MediaType("application", "json", DEFAULT_CHARSET));
} }
/** /**
...@@ -73,12 +78,6 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo ...@@ -73,12 +78,6 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo
this.objectMapper = objectMapper; this.objectMapper = objectMapper;
} }
/** Sets the {@code JsonEncoding} for this converter. By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used. */
public void setEncoding(JsonEncoding encoding) {
Assert.notNull(encoding, "'encoding' must not be null");
this.encoding = encoding;
}
/** /**
* Indicates whether the JSON output by this view should be prefixed with "{} &&". Default is false. * Indicates whether the JSON output by this view should be prefixed with "{} &&". Default is false.
* *
...@@ -91,30 +90,50 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo ...@@ -91,30 +90,50 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo
} }
@Override @Override
public boolean supports(Class<? extends T> clazz) { public boolean canRead(Class<?> clazz, MediaType mediaType) {
return objectMapper.canSerialize(clazz); JavaType javaType = TypeFactory.fromClass(clazz);
return objectMapper.canDeserialize(javaType) && isSupported(mediaType);
} }
@Override @Override
protected T readInternal(Class<T> clazz, HttpInputMessage inputMessage) public boolean canWrite(Class<?> clazz, MediaType mediaType) {
throws IOException, HttpMessageNotReadableException { return objectMapper.canSerialize(clazz) && isSupported(mediaType);
return objectMapper.readValue(inputMessage.getBody(), clazz); }
@Override
protected boolean supports(Class<?> clazz) {
// should not be called, since we override canRead/Write
throw new UnsupportedOperationException();
} }
@Override @Override
protected MediaType getDefaultContentType(T t) { protected Object readInternal(Class<Object> clazz, HttpInputMessage inputMessage)
Charset charset = Charset.forName(encoding.getJavaName()); throws IOException, HttpMessageNotReadableException {
return new MediaType("application", "json", charset); return objectMapper.readValue(inputMessage.getBody(), clazz);
} }
@Override @Override
protected void writeInternal(T t, HttpOutputMessage outputMessage) protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException { throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator = JsonGenerator jsonGenerator =
objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding); objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
if (prefixJson) { if (prefixJson) {
jsonGenerator.writeRaw("{} && "); jsonGenerator.writeRaw("{} && ");
} }
objectMapper.writeValue(jsonGenerator, t); objectMapper.writeValue(jsonGenerator, o);
} }
private JsonEncoding getEncoding(MediaType contentType) {
if (contentType != null && contentType.getCharSet() != null) {
Charset charset = contentType.getCharSet();
for (JsonEncoding encoding : JsonEncoding.values()) {
if (charset.name().equals(encoding.getJavaName())) {
return encoding;
}
}
}
return JsonEncoding.UTF8;
}
} }
...@@ -21,6 +21,7 @@ import java.nio.charset.Charset; ...@@ -21,6 +21,7 @@ import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Before; import org.junit.Before;
...@@ -33,25 +34,33 @@ import org.springframework.http.MockHttpOutputMessage; ...@@ -33,25 +34,33 @@ import org.springframework.http.MockHttpOutputMessage;
/** @author Arjen Poutsma */ /** @author Arjen Poutsma */
public class MappingJacksonHttpMessageConverterTests { public class MappingJacksonHttpMessageConverterTests {
private MappingJacksonHttpMessageConverter<MyBean> converter; private MappingJacksonHttpMessageConverter converter;
@Before @Before
public void setUp() { public void setUp() {
converter = new MappingJacksonHttpMessageConverter<MyBean>(); converter = new MappingJacksonHttpMessageConverter();
} }
@Test @Test
public void supports() { public void canRead() {
assertTrue(converter.supports(MyBean.class)); assertTrue(converter.canRead(MyBean.class, new MediaType("application", "json")));
assertTrue(converter.canRead(Map.class, new MediaType("application", "json")));
} }
@Test @Test
public void canWrite() {
assertTrue(converter.canWrite(MyBean.class, new MediaType("application", "json")));
assertTrue(converter.canWrite(Map.class, new MediaType("application", "json")));
}
@Test
@SuppressWarnings("unchecked")
public void readTyped() throws IOException { public void readTyped() throws IOException {
String body = String body =
"{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
inputMessage.getHeaders().setContentType(new MediaType("application", "json")); inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
MyBean result = converter.read(MyBean.class, inputMessage); MyBean result = (MyBean) converter.read((Class) MyBean.class, inputMessage);
assertEquals("Foo", result.getString()); assertEquals("Foo", result.getString());
assertEquals(42, result.getNumber()); assertEquals(42, result.getNumber());
assertEquals(42F, result.getFraction(), 0F); assertEquals(42F, result.getFraction(), 0F);
...@@ -61,14 +70,13 @@ public class MappingJacksonHttpMessageConverterTests { ...@@ -61,14 +70,13 @@ public class MappingJacksonHttpMessageConverterTests {
} }
@Test @Test
@SuppressWarnings({"unchecked"}) @SuppressWarnings("unchecked")
public void readUntyped() throws IOException { public void readUntyped() throws IOException {
MappingJacksonHttpMessageConverter<HashMap> converter = new MappingJacksonHttpMessageConverter<HashMap>();
String body = String body =
"{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
inputMessage.getHeaders().setContentType(new MediaType("application", "json")); inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
HashMap<String, Object> result = converter.read(HashMap.class, inputMessage); HashMap<String, Object> result = (HashMap<String, Object>) converter.read((Class)HashMap.class, inputMessage);
assertEquals("Foo", result.get("string")); assertEquals("Foo", result.get("string"));
assertEquals(42, result.get("number")); assertEquals(42, result.get("number"));
assertEquals(42D, (Double) result.get("fraction"), 0D); assertEquals(42D, (Double) result.get("fraction"), 0D);
...@@ -103,6 +111,18 @@ public class MappingJacksonHttpMessageConverterTests { ...@@ -103,6 +111,18 @@ public class MappingJacksonHttpMessageConverterTests {
outputMessage.getHeaders().getContentType()); outputMessage.getHeaders().getContentType());
} }
@Test
public void writeUTF16() throws IOException {
Charset utf16 = Charset.forName("UTF-16BE");
MediaType contentType = new MediaType("application", "json", utf16);
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
String body = "H\u00e9llo W\u00f6rld";
converter.write(body, contentType, outputMessage);
assertEquals("Invalid result", "\"" + body + "\"", outputMessage.getBodyAsString(utf16));
assertEquals("Invalid content-type", contentType, outputMessage.getHeaders().getContentType());
}
public static class MyBean { public static class MyBean {
private String string; private String string;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册