Skip to content

Commit eb0aae0

Browse files
committed
Respect MediaType charset in Jackson converters
Before this commit, AbstractJackson2HttpMessageConverter and subclasses did not check media type encoding in the canRead and canWrite methods. As a result, the converter reported that it can write (for instance) "application/json;charset=ISO-8859-1", but in practice wrote the default charset (UTF-8). This commit fixes that bug. See: gh-25076
1 parent a61ab5d commit eb0aae0

File tree

4 files changed

+51
-4
lines changed

4 files changed

+51
-4
lines changed

spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
import java.nio.charset.Charset;
2222
import java.util.Arrays;
2323
import java.util.Collections;
24+
import java.util.EnumSet;
25+
import java.util.Map;
2426
import java.util.concurrent.atomic.AtomicReference;
27+
import java.util.function.Function;
28+
import java.util.stream.Collectors;
2529

2630
import com.fasterxml.jackson.core.JsonEncoding;
2731
import com.fasterxml.jackson.core.JsonGenerator;
@@ -68,6 +72,8 @@
6872
*/
6973
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
7074

75+
private static final Map<String, JsonEncoding> ENCODINGS = jsonEncodings();
76+
7177
/**
7278
* The default charset used by the converter.
7379
*/
@@ -167,6 +173,14 @@ public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable Med
167173
return false;
168174
}
169175

176+
@Override
177+
protected boolean canRead(@Nullable MediaType mediaType) {
178+
if (!super.canRead(mediaType)) {
179+
return false;
180+
}
181+
return checkEncoding(mediaType);
182+
}
183+
170184
@Override
171185
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
172186
if (!canWrite(mediaType)) {
@@ -180,6 +194,14 @@ public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
180194
return false;
181195
}
182196

197+
@Override
198+
protected boolean canWrite(@Nullable MediaType mediaType) {
199+
if (!super.canWrite(mediaType)) {
200+
return false;
201+
}
202+
return checkEncoding(mediaType);
203+
}
204+
183205
/**
184206
* Determine whether to log the given exception coming from a
185207
* {@link ObjectMapper#canDeserialize} / {@link ObjectMapper#canSerialize} check.
@@ -211,6 +233,14 @@ else if (logger.isDebugEnabled()) {
211233
}
212234
}
213235

236+
private boolean checkEncoding(@Nullable MediaType mediaType) {
237+
if (mediaType != null && mediaType.getCharset() != null) {
238+
Charset charset = mediaType.getCharset();
239+
return ENCODINGS.containsKey(charset.name());
240+
}
241+
return true;
242+
}
243+
214244
@Override
215245
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
216246
throws IOException, HttpMessageNotReadableException {
@@ -333,10 +363,9 @@ protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
333363
protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) {
334364
if (contentType != null && contentType.getCharset() != null) {
335365
Charset charset = contentType.getCharset();
336-
for (JsonEncoding encoding : JsonEncoding.values()) {
337-
if (charset.name().equals(encoding.getJavaName())) {
338-
return encoding;
339-
}
366+
JsonEncoding encoding = ENCODINGS.get(charset.name());
367+
if (encoding != null) {
368+
return encoding;
340369
}
341370
}
342371
return JsonEncoding.UTF8;
@@ -359,4 +388,9 @@ protected Long getContentLength(Object object, @Nullable MediaType contentType)
359388
return super.getContentLength(object, contentType);
360389
}
361390

391+
private static Map<String, JsonEncoding> jsonEncodings() {
392+
return EnumSet.allOf(JsonEncoding.class).stream()
393+
.collect(Collectors.toMap(JsonEncoding::getJavaName, Function.identity()));
394+
}
395+
362396
}

spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,16 @@ public class MappingJackson2HttpMessageConverterTests {
6464
public void canRead() {
6565
assertThat(converter.canRead(MyBean.class, new MediaType("application", "json"))).isTrue();
6666
assertThat(converter.canRead(Map.class, new MediaType("application", "json"))).isTrue();
67+
assertThat(converter.canRead(MyBean.class, new MediaType("application", "json", StandardCharsets.UTF_8))).isTrue();
68+
assertThat(converter.canRead(MyBean.class, new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isFalse();
6769
}
6870

6971
@Test
7072
public void canWrite() {
7173
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "json"))).isTrue();
7274
assertThat(converter.canWrite(Map.class, new MediaType("application", "json"))).isTrue();
75+
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "json", StandardCharsets.UTF_8))).isTrue();
76+
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isFalse();
7377
}
7478

7579
@Test // SPR-7905

spring-web/src/test/java/org/springframework/http/converter/smile/MappingJackson2SmileHttpMessageConverterTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.http.converter.smile;
1818

1919
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
2021

2122
import com.fasterxml.jackson.databind.ObjectMapper;
2223
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
@@ -45,13 +46,17 @@ public void canRead() {
4546
assertThat(converter.canRead(MyBean.class, new MediaType("application", "x-jackson-smile"))).isTrue();
4647
assertThat(converter.canRead(MyBean.class, new MediaType("application", "json"))).isFalse();
4748
assertThat(converter.canRead(MyBean.class, new MediaType("application", "xml"))).isFalse();
49+
assertThat(converter.canRead(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.UTF_8))).isTrue();
50+
assertThat(converter.canRead(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.ISO_8859_1))).isFalse();
4851
}
4952

5053
@Test
5154
public void canWrite() {
5255
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "x-jackson-smile"))).isTrue();
5356
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "json"))).isFalse();
5457
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "xml"))).isFalse();
58+
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.UTF_8))).isTrue();
59+
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "x-jackson-smile", StandardCharsets.ISO_8859_1))).isFalse();
5560
}
5661

5762
@Test

spring-web/src/test/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverterTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,17 @@ public void canRead() {
5050
assertThat(converter.canRead(MyBean.class, new MediaType("application", "xml"))).isTrue();
5151
assertThat(converter.canRead(MyBean.class, new MediaType("text", "xml"))).isTrue();
5252
assertThat(converter.canRead(MyBean.class, new MediaType("application", "soap+xml"))).isTrue();
53+
assertThat(converter.canRead(MyBean.class, new MediaType("text", "xml", StandardCharsets.UTF_8))).isTrue();
54+
assertThat(converter.canRead(MyBean.class, new MediaType("text", "xml", StandardCharsets.ISO_8859_1))).isFalse();
5355
}
5456

5557
@Test
5658
public void canWrite() {
5759
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "xml"))).isTrue();
5860
assertThat(converter.canWrite(MyBean.class, new MediaType("text", "xml"))).isTrue();
5961
assertThat(converter.canWrite(MyBean.class, new MediaType("application", "soap+xml"))).isTrue();
62+
assertThat(converter.canWrite(MyBean.class, new MediaType("text", "xml", StandardCharsets.UTF_8))).isTrue();
63+
assertThat(converter.canWrite(MyBean.class, new MediaType("text", "xml", StandardCharsets.ISO_8859_1))).isFalse();
6064
}
6165

6266
@Test

0 commit comments

Comments
 (0)