Skip to content

Commit fe081b1

Browse files
committed
Add Gson converter immediately before default Jackson converter
Previously, when the preferred json mapper was set to Gson, the Gson HTTP message converter was added before any other converters. This changed the form of String responses that were already valid. When Jackson is in use, a string converter is used as it appears earlier in the list than the Jackson converter. When the mapper is switched to Gson, the Gson converter is added first in the list of converters and the Strong converter is no longer used. This results in the String, that was already valid JSON, being converted again. This changes its form as quotes are escaped, etc. This commit updates HttpMessageConverters so that the Gson converter is added to the list immediately before the default Jackson converter. This is done by considering the Gson converter to be an equivalent of the Jackson converter. Fixes gh-27354
1 parent 2f54198 commit fe081b1

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/HttpMessageConverters.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,8 +20,10 @@
2020
import java.util.Arrays;
2121
import java.util.Collection;
2222
import java.util.Collections;
23+
import java.util.HashMap;
2324
import java.util.Iterator;
2425
import java.util.List;
26+
import java.util.Map;
2527

2628
import org.springframework.http.converter.HttpMessageConverter;
2729
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
@@ -63,6 +65,15 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
6365
NON_REPLACING_CONVERTERS = Collections.unmodifiableList(nonReplacingConverters);
6466
}
6567

68+
private static final Map<Class<?>, Class<?>> EQUIVALENT_CONVERTERS;
69+
70+
static {
71+
Map<Class<?>, Class<?>> equivalentConverters = new HashMap<>();
72+
putIfExists(equivalentConverters, "org.springframework.http.converter.json.MappingJackson2HttpMessageConverter",
73+
"org.springframework.http.converter.json.GsonHttpMessageConverter");
74+
EQUIVALENT_CONVERTERS = Collections.unmodifiableMap(equivalentConverters);
75+
}
76+
6677
private final List<HttpMessageConverter<?>> converters;
6778

6879
/**
@@ -132,7 +143,12 @@ private boolean isReplacement(HttpMessageConverter<?> defaultConverter, HttpMess
132143
return false;
133144
}
134145
}
135-
return ClassUtils.isAssignableValue(defaultConverter.getClass(), candidate);
146+
Class<?> converterClass = defaultConverter.getClass();
147+
if (ClassUtils.isAssignableValue(converterClass, candidate)) {
148+
return true;
149+
}
150+
Class<?> equivalentClass = EQUIVALENT_CONVERTERS.get(converterClass);
151+
return equivalentClass != null && ClassUtils.isAssignableValue(equivalentClass, candidate);
136152
}
137153

138154
private void configurePartConverters(AllEncompassingFormHttpMessageConverter formConverter,
@@ -220,4 +236,13 @@ private static void addClassIfExists(List<Class<?>> list, String className) {
220236
}
221237
}
222238

239+
private static void putIfExists(Map<Class<?>, Class<?>> map, String keyClassName, String valueClassName) {
240+
try {
241+
map.put(Class.forName(keyClassName), Class.forName(valueClassName));
242+
}
243+
catch (ClassNotFoundException | NoClassDefFoundError ex) {
244+
// Ignore
245+
}
246+
}
247+
223248
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.Collection;
2121
import java.util.List;
22+
import java.util.stream.Collectors;
2223

2324
import org.junit.jupiter.api.Test;
2425

@@ -28,6 +29,7 @@
2829
import org.springframework.http.converter.ResourceRegionHttpMessageConverter;
2930
import org.springframework.http.converter.StringHttpMessageConverter;
3031
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
32+
import org.springframework.http.converter.json.GsonHttpMessageConverter;
3133
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
3234
import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
3335
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
@@ -81,6 +83,16 @@ void addBeforeExistingConverter() {
8183
assertThat(converters.getConverters().indexOf(converter1)).isNotEqualTo(0);
8284
}
8385

86+
@Test
87+
void addBeforeExistingEquivalentConverter() {
88+
GsonHttpMessageConverter converter1 = new GsonHttpMessageConverter();
89+
HttpMessageConverters converters = new HttpMessageConverters(converter1);
90+
List<Class<?>> converterClasses = converters.getConverters().stream().map(HttpMessageConverter::getClass)
91+
.collect(Collectors.toList());
92+
assertThat(converterClasses).containsSequence(GsonHttpMessageConverter.class,
93+
MappingJackson2HttpMessageConverter.class);
94+
}
95+
8496
@Test
8597
void addNewConverters() {
8698
HttpMessageConverter<?> converter1 = mock(HttpMessageConverter.class);

0 commit comments

Comments
 (0)