Skip to content

Commit f328bfc

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-22764
1 parent f6b2c0f commit f328bfc

File tree

2 files changed

+69
-14
lines changed

2 files changed

+69
-14
lines changed

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

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import org.springframework.context.ApplicationContext;
5757
import org.springframework.util.Assert;
5858
import org.springframework.util.ClassUtils;
59+
import org.springframework.util.LinkedMultiValueMap;
60+
import org.springframework.util.MultiValueMap;
5961
import org.springframework.util.StreamUtils;
6062
import org.springframework.util.StringUtils;
6163

@@ -599,11 +601,10 @@ public <T extends ObjectMapper> T build() {
599601
public void configure(ObjectMapper objectMapper) {
600602
Assert.notNull(objectMapper, "ObjectMapper must not be null");
601603

602-
Map<Object, Module> modulesToRegister = new LinkedHashMap<Object, Module>();
604+
MultiValueMap<Object, Module> modulesToRegister = new LinkedMultiValueMap<Object, Module>();
603605
if (this.findModulesViaServiceLoader) {
604-
// Jackson 2.2+
605606
for (Module module : ObjectMapper.findModules(this.moduleClassLoader)) {
606-
modulesToRegister.put(module.getTypeId(), module);
607+
registerModule(module, modulesToRegister);
607608
}
608609
}
609610
else if (this.findWellKnownModules) {
@@ -612,18 +613,19 @@ else if (this.findWellKnownModules) {
612613

613614
if (this.modules != null) {
614615
for (Module module : this.modules) {
615-
modulesToRegister.put(module.getTypeId(), module);
616+
registerModule(module, modulesToRegister);
616617
}
617618
}
618619
if (this.moduleClasses != null) {
619620
for (Class<? extends Module> moduleClass : this.moduleClasses) {
620-
Module module = BeanUtils.instantiateClass(moduleClass);
621-
modulesToRegister.put(module.getTypeId(), module);
621+
registerModule(BeanUtils.instantiateClass(moduleClass), modulesToRegister);
622622
}
623623
}
624624
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
625-
for (Module module : modulesToRegister.values()) {
626-
objectMapper.registerModule(module);
625+
for (List<Module> nestedModules : modulesToRegister.values()) {
626+
for (Module module : nestedModules) {
627+
objectMapper.registerModule(module);
628+
}
627629
}
628630

629631
if (this.dateFormat != null) {
@@ -678,6 +680,15 @@ else if (this.applicationContext != null) {
678680
}
679681
}
680682

683+
private void registerModule(Module module, MultiValueMap<Object, Module> modulesToRegister) {
684+
if (module.getTypeId() == null) {
685+
modulesToRegister.add(SimpleModule.class.getName(), module);
686+
}
687+
else {
688+
modulesToRegister.set(module.getTypeId(), module);
689+
}
690+
}
691+
681692

682693
// Any change to this method should be also applied to spring-jms and spring-messaging
683694
// MappingJackson2MessageConverter default constructors
@@ -726,14 +737,14 @@ else if (feature instanceof MapperFeature) {
726737
}
727738

728739
@SuppressWarnings("unchecked")
729-
private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRegister) {
740+
private void registerWellKnownModulesIfAvailable(MultiValueMap<Object, Module> modulesToRegister) {
730741
// Java 7 java.nio.file.Path class present?
731742
if (ClassUtils.isPresent("java.nio.file.Path", this.moduleClassLoader)) {
732743
try {
733744
Class<? extends Module> jdk7ModuleClass = (Class<? extends Module>)
734745
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk7.Jdk7Module", this.moduleClassLoader);
735746
Module jdk7Module = BeanUtils.instantiateClass(jdk7ModuleClass);
736-
modulesToRegister.put(jdk7Module.getTypeId(), jdk7Module);
747+
modulesToRegister.set(jdk7Module.getTypeId(), jdk7Module);
737748
}
738749
catch (ClassNotFoundException ex) {
739750
// jackson-datatype-jdk7 not available
@@ -746,7 +757,7 @@ private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRe
746757
Class<? extends Module> jdk8ModuleClass = (Class<? extends Module>)
747758
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", this.moduleClassLoader);
748759
Module jdk8Module = BeanUtils.instantiateClass(jdk8ModuleClass);
749-
modulesToRegister.put(jdk8Module.getTypeId(), jdk8Module);
760+
modulesToRegister.set(jdk8Module.getTypeId(), jdk8Module);
750761
}
751762
catch (ClassNotFoundException ex) {
752763
// jackson-datatype-jdk8 not available
@@ -759,7 +770,7 @@ private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRe
759770
Class<? extends Module> javaTimeModuleClass = (Class<? extends Module>)
760771
ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", this.moduleClassLoader);
761772
Module javaTimeModule = BeanUtils.instantiateClass(javaTimeModuleClass);
762-
modulesToRegister.put(javaTimeModule.getTypeId(), javaTimeModule);
773+
modulesToRegister.set(javaTimeModule.getTypeId(), javaTimeModule);
763774
}
764775
catch (ClassNotFoundException ex) {
765776
// jackson-datatype-jsr310 not available
@@ -772,7 +783,7 @@ private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRe
772783
Class<? extends Module> jodaModuleClass = (Class<? extends Module>)
773784
ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", this.moduleClassLoader);
774785
Module jodaModule = BeanUtils.instantiateClass(jodaModuleClass);
775-
modulesToRegister.put(jodaModule.getTypeId(), jodaModule);
786+
modulesToRegister.set(jodaModule.getTypeId(), jodaModule);
776787
}
777788
catch (ClassNotFoundException ex) {
778789
// jackson-datatype-joda not available
@@ -785,7 +796,7 @@ private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRe
785796
Class<? extends Module> kotlinModuleClass = (Class<? extends Module>)
786797
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader);
787798
Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass);
788-
modulesToRegister.put(kotlinModule.getTypeId(), kotlinModule);
799+
modulesToRegister.set(kotlinModule.getTypeId(), kotlinModule);
789800
}
790801
catch (ClassNotFoundException ex) {
791802
// jackson-module-kotlin not available

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;
@@ -309,6 +310,24 @@ public void overrideWellKnownModuleWithModule() throws IOException {
309310
assertNotNull(demoPojo.getOffsetDateTime());
310311
}
311312

313+
@Test // gh-22740
314+
public void registerMultipleModulesWithNullTypeId() {
315+
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
316+
SimpleModule fooModule = new SimpleModule();
317+
fooModule.addSerializer(new FooSerializer());
318+
SimpleModule barModule = new SimpleModule();
319+
barModule.addSerializer(new BarSerializer());
320+
builder.modulesToInstall(fooModule, barModule);
321+
ObjectMapper objectMapper = builder.build();
322+
assertEquals(1, StreamSupport
323+
.stream(getSerializerFactoryConfig(objectMapper).serializers().spliterator(), false)
324+
.filter(s -> s.findSerializer(null, SimpleType.construct(Foo.class), null) != null)
325+
.count());
326+
assertEquals(1, StreamSupport
327+
.stream(getSerializerFactoryConfig(objectMapper).serializers().spliterator(), false)
328+
.filter(s -> s.findSerializer(null, SimpleType.construct(Bar.class), null) != null)
329+
.count());
330+
}
312331

313332
private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) {
314333
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
@@ -606,4 +625,29 @@ public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
606625

607626
}
608627

628+
static class Foo {}
629+
630+
static class Bar {}
631+
632+
static class FooSerializer extends JsonSerializer<Foo> {
633+
@Override
634+
public void serialize(Foo value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
635+
}
636+
637+
@Override
638+
public Class<Foo> handledType() {
639+
return Foo.class;
640+
}
641+
}
642+
643+
static class BarSerializer extends JsonSerializer<Bar> {
644+
@Override
645+
public void serialize(Bar value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
646+
}
647+
@Override
648+
public Class<Bar> handledType() {
649+
return Bar.class;
650+
}
651+
}
652+
609653
}

0 commit comments

Comments
 (0)