Skip to content

Commit da0754e

Browse files
committed
Fix a regression in Jackson builder module registration
This commit brings back the support for registration of multiple Jackson modules with a null typeId. Closes gh-22763
1 parent 0e0e784 commit da0754e

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

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

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.text.DateFormat;
2020
import java.text.SimpleDateFormat;
21+
import java.util.ArrayList;
2122
import java.util.Arrays;
2223
import java.util.HashMap;
2324
import java.util.LinkedHashMap;
@@ -61,6 +62,8 @@
6162
import org.springframework.lang.Nullable;
6263
import org.springframework.util.Assert;
6364
import org.springframework.util.ClassUtils;
65+
import org.springframework.util.LinkedMultiValueMap;
66+
import org.springframework.util.MultiValueMap;
6467
import org.springframework.util.StringUtils;
6568
import org.springframework.util.xml.StaxUtils;
6669

@@ -617,24 +620,27 @@ public <T extends ObjectMapper> T build() {
617620
public void configure(ObjectMapper objectMapper) {
618621
Assert.notNull(objectMapper, "ObjectMapper must not be null");
619622

620-
Map<Object, Module> modulesToRegister = new LinkedHashMap<>();
623+
MultiValueMap<Object, Module> modulesToRegister = new LinkedMultiValueMap<>();
621624
if (this.findModulesViaServiceLoader) {
622-
ObjectMapper.findModules(this.moduleClassLoader).forEach(module -> modulesToRegister.put(module.getTypeId(), module));
625+
ObjectMapper.findModules(this.moduleClassLoader).forEach(module -> registerModule(module, modulesToRegister));
623626
}
624627
else if (this.findWellKnownModules) {
625628
registerWellKnownModulesIfAvailable(modulesToRegister);
626629
}
627630

628631
if (this.modules != null) {
629-
this.modules.forEach(module -> modulesToRegister.put(module.getTypeId(), module));
632+
this.modules.forEach(module -> registerModule(module, modulesToRegister));
630633
}
631634
if (this.moduleClasses != null) {
632635
for (Class<? extends Module> moduleClass : this.moduleClasses) {
633-
Module module = BeanUtils.instantiateClass(moduleClass);
634-
modulesToRegister.put(module.getTypeId(), module);
636+
registerModule(BeanUtils.instantiateClass(moduleClass), modulesToRegister);
635637
}
636638
}
637-
objectMapper.registerModules(modulesToRegister.values());
639+
List<Module> modules = new ArrayList<>();
640+
for (List<Module> nestedModules : modulesToRegister.values()) {
641+
modules.addAll(nestedModules);
642+
}
643+
objectMapper.registerModules(modules);
638644

639645
if (this.dateFormat != null) {
640646
objectMapper.setDateFormat(this.dateFormat);
@@ -684,6 +690,15 @@ else if (this.applicationContext != null) {
684690
}
685691
}
686692

693+
private void registerModule(Module module, MultiValueMap<Object, Module> modulesToRegister) {
694+
if (module.getTypeId() == null) {
695+
modulesToRegister.add(SimpleModule.class.getName(), module);
696+
}
697+
else {
698+
modulesToRegister.set(module.getTypeId(), module);
699+
}
700+
}
701+
687702

688703
// Any change to this method should be also applied to spring-jms and spring-messaging
689704
// MappingJackson2MessageConverter default constructors
@@ -730,12 +745,12 @@ else if (feature instanceof MapperFeature) {
730745
}
731746

732747
@SuppressWarnings("unchecked")
733-
private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRegister) {
748+
private void registerWellKnownModulesIfAvailable(MultiValueMap<Object, Module> modulesToRegister) {
734749
try {
735750
Class<? extends Module> jdk8ModuleClass = (Class<? extends Module>)
736751
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", this.moduleClassLoader);
737752
Module jdk8Module = BeanUtils.instantiateClass(jdk8ModuleClass);
738-
modulesToRegister.put(jdk8Module.getTypeId(), jdk8Module);
753+
modulesToRegister.set(jdk8Module.getTypeId(), jdk8Module);
739754
}
740755
catch (ClassNotFoundException ex) {
741756
// jackson-datatype-jdk8 not available
@@ -745,7 +760,7 @@ private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRe
745760
Class<? extends Module> javaTimeModuleClass = (Class<? extends Module>)
746761
ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", this.moduleClassLoader);
747762
Module javaTimeModule = BeanUtils.instantiateClass(javaTimeModuleClass);
748-
modulesToRegister.put(javaTimeModule.getTypeId(), javaTimeModule);
763+
modulesToRegister.set(javaTimeModule.getTypeId(), javaTimeModule);
749764
}
750765
catch (ClassNotFoundException ex) {
751766
// jackson-datatype-jsr310 not available
@@ -757,7 +772,7 @@ private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRe
757772
Class<? extends Module> jodaModuleClass = (Class<? extends Module>)
758773
ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", this.moduleClassLoader);
759774
Module jodaModule = BeanUtils.instantiateClass(jodaModuleClass);
760-
modulesToRegister.put(jodaModule.getTypeId(), jodaModule);
775+
modulesToRegister.set(jodaModule.getTypeId(), jodaModule);
761776
}
762777
catch (ClassNotFoundException ex) {
763778
// jackson-datatype-joda not available
@@ -770,7 +785,7 @@ private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRe
770785
Class<? extends Module> kotlinModuleClass = (Class<? extends Module>)
771786
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader);
772787
Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass);
773-
modulesToRegister.put(kotlinModule.getTypeId(), kotlinModule);
788+
modulesToRegister.set(kotlinModule.getTypeId(), kotlinModule);
774789
}
775790
catch (ClassNotFoundException ex) {
776791
if (!kotlinWarningLogged) {

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.Map;
3434
import java.util.Optional;
3535
import java.util.TimeZone;
36+
import java.util.stream.StreamSupport;
3637

3738
import com.fasterxml.jackson.annotation.JsonFilter;
3839
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -325,6 +326,24 @@ public void overrideWellKnownModuleWithModule() throws IOException {
325326
assertNotNull(demoPojo.getOffsetDateTime());
326327
}
327328

329+
@Test // gh-22740
330+
public void registerMultipleModulesWithNullTypeId() {
331+
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
332+
SimpleModule fooModule = new SimpleModule();
333+
fooModule.addSerializer(new FooSerializer());
334+
SimpleModule barModule = new SimpleModule();
335+
barModule.addSerializer(new BarSerializer());
336+
builder.modulesToInstall(fooModule, barModule);
337+
ObjectMapper objectMapper = builder.build();
338+
assertEquals(1, StreamSupport
339+
.stream(getSerializerFactoryConfig(objectMapper).serializers().spliterator(), false)
340+
.filter(s -> s.findSerializer(null, SimpleType.construct(Foo.class), null) != null)
341+
.count());
342+
assertEquals(1, StreamSupport
343+
.stream(getSerializerFactoryConfig(objectMapper).serializers().spliterator(), false)
344+
.filter(s -> s.findSerializer(null, SimpleType.construct(Bar.class), null) != null)
345+
.count());
346+
}
328347

329348
private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) {
330349
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
@@ -651,4 +670,29 @@ public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
651670

652671
}
653672

673+
static class Foo {}
674+
675+
static class Bar {}
676+
677+
static class FooSerializer extends JsonSerializer<Foo> {
678+
@Override
679+
public void serialize(Foo value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
680+
}
681+
682+
@Override
683+
public Class<Foo> handledType() {
684+
return Foo.class;
685+
}
686+
}
687+
688+
static class BarSerializer extends JsonSerializer<Bar> {
689+
@Override
690+
public void serialize(Bar value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
691+
}
692+
@Override
693+
public Class<Bar> handledType() {
694+
return Bar.class;
695+
}
696+
}
697+
654698
}

0 commit comments

Comments
 (0)