From 485bd7c6367147f9387545f30d3346bdef93fca9 Mon Sep 17 00:00:00 2001 From: Ahmed El amraouiyine Date: Mon, 15 Jun 2026 22:25:31 +0100 Subject: [PATCH 1/2] Bind empty strings to empty maps Align `MapBinder` with `IndexedElementsBinder` to ensure that empty strings are bound as empty `Map` instances rather than throwing a `ConverterNotFoundException`. See gh-50773 Signed-off-by: Ahmed El amraouiyine --- .../context/properties/bind/MapBinder.java | 4 ++++ .../properties/bind/MapBinderTests.java | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index f1e57fa32ee..a8aa438edf6 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -33,6 +33,7 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource; import org.springframework.core.CollectionFactory; import org.springframework.core.ResolvableType; +import org.springframework.util.ObjectUtils; /** * {@link AggregateBinder} for Maps. @@ -64,6 +65,9 @@ class MapBinder extends AggregateBinder> { if (property != null) { getContext().setConfigurationProperty(property); Object result = getContext().getPlaceholdersResolver().resolvePlaceholders(property.getValue()); + if (ObjectUtils.isEmpty(result)) { + return createMap(target); + } return getContext().getConverter().convert(result, target); } } diff --git a/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java b/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java index 7b591d785a1..1cd94635604 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java @@ -274,6 +274,15 @@ class MapBinderTests { assertThat(result.isBound()).isFalse(); } + @Test + void bindToMapWhenEmptyStringShouldReturnEmptyMap() { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo", ""); + this.sources.add(source); + Map result = this.binder.bind("foo", STRING_STRING_MAP).get(); + assertThat(result).isEmpty(); + } + @Test void bindToMapShouldConvertKey() { MockConfigurationPropertySource source = new MockConfigurationPropertySource(); @@ -510,6 +519,17 @@ class MapBinderTests { assertThat(foo2.getValue()).isEqualTo("three"); } + @Test + void nestedMapsWhenEmptyStringShouldReturnEmptyMap() { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.value", "one"); + source.put("foo.foos", ""); + this.sources.add(source); + BindResult foo = this.binder.bind("foo", NestableFoo.class); + assertThat(foo.get().getValue()).isEqualTo("one"); + assertThat(foo.get().getFoos()).isEmpty(); + } + @Test void bindToMapWithCustomConverter() { DefaultConversionService conversionService = new DefaultConversionService(); From 12eb46696aa65e0e8dc11ed305bd4685110c127b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 20 Jun 2026 19:43:51 -0700 Subject: [PATCH 2/2] Polish 'Bind empty strings to empty maps' See gh-50773 --- .../boot/context/properties/bind/MapBinder.java | 4 ++-- .../boot/context/properties/bind/MapBinderTests.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index a8aa438edf6..db40be47d8c 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -33,13 +33,13 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource; import org.springframework.core.CollectionFactory; import org.springframework.core.ResolvableType; -import org.springframework.util.ObjectUtils; /** * {@link AggregateBinder} for Maps. * * @author Phillip Webb * @author Madhura Bhave + * @author Ahmed El Amraouiyine */ class MapBinder extends AggregateBinder> { @@ -65,7 +65,7 @@ class MapBinder extends AggregateBinder> { if (property != null) { getContext().setConfigurationProperty(property); Object result = getContext().getPlaceholdersResolver().resolvePlaceholders(property.getValue()); - if (ObjectUtils.isEmpty(result)) { + if (result instanceof CharSequence charSequence && charSequence.isEmpty()) { return createMap(target); } return getContext().getConverter().convert(result, target); diff --git a/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java b/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java index 1cd94635604..3e616547227 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java @@ -65,6 +65,7 @@ import static org.mockito.Mockito.mock; * * @author Phillip Webb * @author Madhura Bhave + * @author Ahmed El Amraouiyine */ class MapBinderTests {