diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java index c060f243dbac..c043cc606093 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java @@ -69,10 +69,18 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple private static final Map STREAM_SEPARATORS; + private static final Map ENCODINGS; + static { STREAM_SEPARATORS = new HashMap<>(4); STREAM_SEPARATORS.put(MediaType.APPLICATION_STREAM_JSON, NEWLINE_SEPARATOR); STREAM_SEPARATORS.put(MediaType.parseMediaType("application/stream+x-jackson-smile"), new byte[0]); + + ENCODINGS = new HashMap<>(JsonEncoding.values().length); + for (JsonEncoding encoding : JsonEncoding.values()) { + Charset charset = Charset.forName(encoding.getJavaName()); + ENCODINGS.put(charset, encoding); + } } @@ -103,7 +111,16 @@ public void setStreamingMediaTypes(List mediaTypes) { @Override public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) { Class clazz = elementType.toClass(); - return supportsMimeType(mimeType) && (Object.class == clazz || + if (!supportsMimeType(mimeType)) { + return false; + } + if (mimeType != null && mimeType.getCharset() != null) { + Charset charset = mimeType.getCharset(); + if (!ENCODINGS.containsKey(charset)) { + return false; + } + } + return (Object.class == clazz || (!String.class.isAssignableFrom(elementType.resolve(clazz)) && getObjectMapper().canSerialize(clazz))); } @@ -270,10 +287,9 @@ private byte[] streamSeparator(@Nullable MimeType mimeType) { protected JsonEncoding getJsonEncoding(@Nullable MimeType mimeType) { if (mimeType != null && mimeType.getCharset() != null) { Charset charset = mimeType.getCharset(); - for (JsonEncoding encoding : JsonEncoding.values()) { - if (charset.name().equals(encoding.getJavaName())) { - return encoding; - } + JsonEncoding result = ENCODINGS.get(charset); + if (result != null) { + return result; } } return JsonEncoding.UTF8; diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java index 63eabcd75012..6162be280c91 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java @@ -18,18 +18,13 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; -import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collections; -import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonView; -import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -80,9 +75,6 @@ public abstract class Jackson2CodecSupport { new MimeType("application", "json"), new MimeType("application", "*+json"))); - private static final Map ENCODINGS = jsonEncodings(); - - protected final Log logger = HttpLogging.forLogName(getClass()); @@ -115,17 +107,7 @@ protected List getMimeTypes() { protected boolean supportsMimeType(@Nullable MimeType mimeType) { - if (mimeType == null) { - return true; - } - else if (this.mimeTypes.stream().noneMatch(m -> m.isCompatibleWith(mimeType))) { - return false; - } - else if (mimeType.getCharset() != null) { - Charset charset = mimeType.getCharset(); - return ENCODINGS.containsKey(charset.name()); - } - return true; + return (mimeType == null || this.mimeTypes.stream().anyMatch(m -> m.isCompatibleWith(mimeType))); } protected JavaType getJavaType(Type type, @Nullable Class contextClass) { @@ -163,10 +145,4 @@ protected MethodParameter getParameter(ResolvableType type) { @Nullable protected abstract A getAnnotation(MethodParameter parameter, Class annotType); - private static Map jsonEncodings() { - return EnumSet.allOf(JsonEncoding.class).stream() - .collect(Collectors.toMap(JsonEncoding::getJavaName, Function.identity())); - } - - } 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 9ad1a320105f..01de325ff1db 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 @@ -17,8 +17,11 @@ package org.springframework.http.converter.json; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; import java.lang.reflect.Type; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -36,6 +39,7 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.SerializationFeature; @@ -72,7 +76,7 @@ */ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter { - private static final Map ENCODINGS = jsonEncodings(); + private static final Map ENCODINGS = jsonEncodings(); /** * The default charset used by the converter. @@ -173,19 +177,17 @@ public boolean canRead(Type type, @Nullable Class contextClass, @Nullable Med return false; } - @Override - protected boolean canRead(@Nullable MediaType mediaType) { - if (!super.canRead(mediaType)) { - return false; - } - return checkEncoding(mediaType); - } - @Override public boolean canWrite(Class clazz, @Nullable MediaType mediaType) { if (!canWrite(mediaType)) { return false; } + if (mediaType != null && mediaType.getCharset() != null) { + Charset charset = mediaType.getCharset(); + if (!ENCODINGS.containsKey(charset)) { + return false; + } + } AtomicReference causeRef = new AtomicReference<>(); if (this.objectMapper.canSerialize(clazz, causeRef)) { return true; @@ -194,14 +196,6 @@ public boolean canWrite(Class clazz, @Nullable MediaType mediaType) { return false; } - @Override - protected boolean canWrite(@Nullable MediaType mediaType) { - if (!super.canWrite(mediaType)) { - return false; - } - return checkEncoding(mediaType); - } - /** * Determine whether to log the given exception coming from a * {@link ObjectMapper#canDeserialize} / {@link ObjectMapper#canSerialize} check. @@ -233,14 +227,6 @@ else if (logger.isDebugEnabled()) { } } - private boolean checkEncoding(@Nullable MediaType mediaType) { - if (mediaType != null && mediaType.getCharset() != null) { - Charset charset = mediaType.getCharset(); - return ENCODINGS.containsKey(charset.name()); - } - return true; - } - @Override protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { @@ -258,15 +244,31 @@ public Object read(Type type, @Nullable Class contextClass, HttpInputMessage } private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException { + MediaType contentType = inputMessage.getHeaders().getContentType(); + Charset charset = getCharset(contentType); + + boolean isUnicode = ENCODINGS.containsKey(charset); try { if (inputMessage instanceof MappingJacksonInputMessage) { Class deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView(); if (deserializationView != null) { - return this.objectMapper.readerWithView(deserializationView).forType(javaType). - readValue(inputMessage.getBody()); + ObjectReader objectReader = this.objectMapper.readerWithView(deserializationView).forType(javaType); + if (isUnicode) { + return objectReader.readValue(inputMessage.getBody()); + } + else { + Reader reader = new InputStreamReader(inputMessage.getBody(), charset); + return objectReader.readValue(reader); + } } } - return this.objectMapper.readValue(inputMessage.getBody(), javaType); + if (isUnicode) { + return this.objectMapper.readValue(inputMessage.getBody(), javaType); + } + else { + Reader reader = new InputStreamReader(inputMessage.getBody(), charset); + return this.objectMapper.readValue(reader, javaType); + } } catch (InvalidDefinitionException ex) { throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); @@ -276,6 +278,15 @@ private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) th } } + private static Charset getCharset(@Nullable MediaType contentType) { + if (contentType != null && contentType.getCharset() != null) { + return contentType.getCharset(); + } + else { + return StandardCharsets.UTF_8; + } + } + @Override protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { @@ -363,7 +374,7 @@ protected JavaType getJavaType(Type type, @Nullable Class contextClass) { protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) { if (contentType != null && contentType.getCharset() != null) { Charset charset = contentType.getCharset(); - JsonEncoding encoding = ENCODINGS.get(charset.name()); + JsonEncoding encoding = ENCODINGS.get(charset); if (encoding != null) { return encoding; } @@ -388,9 +399,9 @@ protected Long getContentLength(Object object, @Nullable MediaType contentType) return super.getContentLength(object, contentType); } - private static Map jsonEncodings() { + private static Map jsonEncodings() { return EnumSet.allOf(JsonEncoding.class).stream() - .collect(Collectors.toMap(JsonEncoding::getJavaName, Function.identity())); + .collect(Collectors.toMap(encoding -> Charset.forName(encoding.getJavaName()), Function.identity())); } } diff --git a/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborDecoderTests.java index 041ad618b5d4..7eaab7173312 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborDecoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborDecoderTests.java @@ -16,7 +16,6 @@ package org.springframework.http.codec.cbor; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -28,7 +27,6 @@ import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.testfixture.codec.AbstractDecoderTests; -import org.springframework.http.MediaType; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.util.MimeType; import org.springframework.web.testfixture.xml.Pojo; @@ -64,11 +62,6 @@ public void canDecode() { assertThat(decoder.canDecode(ResolvableType.forClass(String.class), null)).isFalse(); assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), APPLICATION_JSON)).isFalse(); - - assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "cbor", StandardCharsets.UTF_8))).isTrue(); - assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "cbor", StandardCharsets.ISO_8859_1))).isFalse(); } @Override diff --git a/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborEncoderTests.java index b9b984581d6e..a2ce8c51a3d1 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborEncoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborEncoderTests.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import com.fasterxml.jackson.databind.ObjectMapper; @@ -29,7 +28,6 @@ import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.testfixture.io.buffer.AbstractLeakCheckingTests; import org.springframework.core.testfixture.io.buffer.DataBufferTestUtils; -import org.springframework.http.MediaType; import org.springframework.http.codec.ServerSentEvent; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.util.MimeType; @@ -75,12 +73,6 @@ public void canEncode() { // SPR-15464 assertThat(this.encoder.canEncode(ResolvableType.NONE, null)).isTrue(); - - - assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "cbor", StandardCharsets.UTF_8))).isTrue(); - assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "cbor", StandardCharsets.ISO_8859_1))).isFalse(); } @Test diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonDecoderTests.java index 12f999a27256..2b8100934de3 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonDecoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonDecoderTests.java @@ -88,7 +88,7 @@ public void canDecode() { assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class), new MediaType("application", "json", StandardCharsets.UTF_8))).isTrue(); assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isFalse(); + new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isTrue(); } @Test // SPR-15866 @@ -235,6 +235,21 @@ public void decodeNonUtf8Encoding() { null); } + @Test + @SuppressWarnings("unchecked") + public void decodeNonUnicode() { + Flux input = Flux.concat( + stringBuffer("{\"føø\":\"bår\"}", StandardCharsets.ISO_8859_1) + ); + + testDecode(input, ResolvableType.forType(new ParameterizedTypeReference>() { + }), + step -> step.assertNext(o -> assertThat((Map) o).containsEntry("føø", "bår")) + .verifyComplete(), + MediaType.parseMediaType("application/json; charset=iso-8859-1"), + null); + } + @Test @SuppressWarnings("unchecked") public void decodeMonoNonUtf8Encoding() { diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileDecoderTests.java index f67772cdae25..9e3404ce1ac9 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileDecoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileDecoderTests.java @@ -16,7 +16,6 @@ package org.springframework.http.codec.json; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -28,7 +27,6 @@ import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.testfixture.codec.AbstractDecoderTests; -import org.springframework.http.MediaType; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.util.MimeType; import org.springframework.web.testfixture.xml.Pojo; @@ -65,12 +63,6 @@ public void canDecode() { assertThat(decoder.canDecode(ResolvableType.forClass(String.class), null)).isFalse(); assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), APPLICATION_JSON)).isFalse(); - - assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "x-jackson-smile", StandardCharsets.UTF_8))).isTrue(); - assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "x-jackson-smile", StandardCharsets.ISO_8859_1))).isFalse(); - } @Override diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileEncoderTests.java index f12171a052e4..32b15ba0b52f 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileEncoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2SmileEncoderTests.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -33,7 +32,6 @@ import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.testfixture.codec.AbstractEncoderTests; -import org.springframework.http.MediaType; import org.springframework.http.codec.ServerSentEvent; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.util.MimeType; @@ -70,11 +68,6 @@ public void canEncode() { assertThat(this.encoder.canEncode(pojoType, STREAM_SMILE_MIME_TYPE)).isTrue(); assertThat(this.encoder.canEncode(pojoType, null)).isTrue(); - assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "x-jackson-smile", StandardCharsets.UTF_8))).isTrue(); - assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), - new MediaType("application", "x-jackson-smile", StandardCharsets.ISO_8859_1))).isFalse(); - // SPR-15464 assertThat(this.encoder.canEncode(ResolvableType.NONE, null)).isTrue(); } 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 c3f25bff6d54..c08ace1907d7 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 @@ -18,6 +18,7 @@ import java.io.IOException; import java.lang.reflect.Type; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; @@ -44,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.within; /** @@ -65,7 +67,7 @@ public void canRead() { assertThat(converter.canRead(MyBean.class, new MediaType("application", "json"))).isTrue(); assertThat(converter.canRead(Map.class, new MediaType("application", "json"))).isTrue(); assertThat(converter.canRead(MyBean.class, new MediaType("application", "json", StandardCharsets.UTF_8))).isTrue(); - assertThat(converter.canRead(MyBean.class, new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isFalse(); + assertThat(converter.canRead(MyBean.class, new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isTrue(); } @Test @@ -439,13 +441,25 @@ public void writeSubTypeList() throws Exception { @Test public void readWithNoDefaultConstructor() throws Exception { String body = "{\"property1\":\"foo\",\"property2\":\"bar\"}"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8)); inputMessage.getHeaders().setContentType(new MediaType("application", "json")); assertThatExceptionOfType(HttpMessageConversionException.class).isThrownBy(() -> converter.read(BeanWithNoDefaultConstructor.class, inputMessage)) .withMessageStartingWith("Type definition error:"); } + @Test + @SuppressWarnings("unchecked") + public void readNonUnicode() throws Exception { + String body = "{\"føø\":\"bår\"}"; + Charset charset = StandardCharsets.ISO_8859_1; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(charset)); + inputMessage.getHeaders().setContentType(new MediaType("application", "json", charset)); + HashMap result = (HashMap) this.converter.read(HashMap.class, inputMessage); + + assertThat(result).containsExactly(entry("føø", "bår")); + } + interface MyInterface { diff --git a/spring-web/src/test/java/org/springframework/http/converter/smile/MappingJackson2SmileHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/smile/MappingJackson2SmileHttpMessageConverterTests.java index 3d6c8912f5c5..2cbb22bdef35 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/smile/MappingJackson2SmileHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/smile/MappingJackson2SmileHttpMessageConverterTests.java @@ -17,7 +17,6 @@ package org.springframework.http.converter.smile; import java.io.IOException; -import java.nio.charset.StandardCharsets; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.smile.SmileFactory; @@ -46,8 +45,6 @@ public void canRead() { assertThat(converter.canRead(MyBean.class, new MediaType("application", "x-jackson-smile"))).isTrue(); assertThat(converter.canRead(MyBean.class, new MediaType("application", "json"))).isFalse(); assertThat(converter.canRead(MyBean.class, new MediaType("application", "xml"))).isFalse(); - assertThat(converter.canRead(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.UTF_8))).isTrue(); - assertThat(converter.canRead(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.ISO_8859_1))).isFalse(); } @Test @@ -55,8 +52,6 @@ public void canWrite() { assertThat(converter.canWrite(MyBean.class, new MediaType("application", "x-jackson-smile"))).isTrue(); assertThat(converter.canWrite(MyBean.class, new MediaType("application", "json"))).isFalse(); assertThat(converter.canWrite(MyBean.class, new MediaType("application", "xml"))).isFalse(); - assertThat(converter.canWrite(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.UTF_8))).isTrue(); - assertThat(converter.canWrite(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.ISO_8859_1))).isFalse(); } @Test diff --git a/spring-web/src/test/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverterTests.java index f39747b26ffd..ab8c617b6f91 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverterTests.java @@ -17,6 +17,7 @@ package org.springframework.http.converter.xml; import java.io.IOException; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import com.fasterxml.jackson.annotation.JsonView; @@ -51,7 +52,7 @@ public void canRead() { assertThat(converter.canRead(MyBean.class, new MediaType("text", "xml"))).isTrue(); assertThat(converter.canRead(MyBean.class, new MediaType("application", "soap+xml"))).isTrue(); assertThat(converter.canRead(MyBean.class, new MediaType("text", "xml", StandardCharsets.UTF_8))).isTrue(); - assertThat(converter.canRead(MyBean.class, new MediaType("text", "xml", StandardCharsets.ISO_8859_1))).isFalse(); + assertThat(converter.canRead(MyBean.class, new MediaType("text", "xml", StandardCharsets.ISO_8859_1))).isTrue(); } @Test @@ -189,6 +190,21 @@ public void readWithXmlBomb() throws IOException { this.converter.read(MyBean.class, inputMessage)); } + @Test + @SuppressWarnings("unchecked") + public void readNonUnicode() throws Exception { + String body = "" + + "føø bår" + + ""; + + Charset charset = StandardCharsets.ISO_8859_1; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(charset)); + inputMessage.getHeaders().setContentType(new MediaType("application", "xml", charset)); + MyBean result = (MyBean) converter.read(MyBean.class, inputMessage); + assertThat(result.getString()).isEqualTo("føø bår"); + } + + public static class MyBean {