diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index 67c1f4b75d4..7e5d083de6b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -24,6 +24,7 @@ import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; +import org.springframework.boot.context.properties.source.ConfigurationPropertyState; import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource; import org.springframework.core.CollectionFactory; import org.springframework.core.ResolvableType; @@ -55,10 +56,12 @@ class MapBinder extends AggregateBinder> { Map map = CollectionFactory.createMap( (target.getValue() == null ? target.getType().resolve() : Map.class), 0); Bindable resolvedTarget = resolveTarget(target); + boolean hasDescendants = getContext().streamSources().anyMatch((source) -> source + .containsDescendantOf(name) == ConfigurationPropertyState.PRESENT); for (ConfigurationPropertySource source : getContext().getSources()) { if (!ConfigurationPropertyName.EMPTY.equals(name)) { ConfigurationProperty property = source.getConfigurationProperty(name); - if (property != null) { + if (property != null && !hasDescendants) { Object value = getContext().getPlaceholdersResolver() .resolvePlaceholders(property.getValue()); return ResolvableTypeDescriptor.forType(target.getType()) diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java index 749b5717f7a..b583fa1967f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java @@ -545,6 +545,22 @@ public class MapBinderTests { assertThat(map.get("b")).isNotNull(); } + @Test + public void bindToMapWithCustomConverterAndChildElements() { + // gh-11892 + DefaultConversionService conversionService = new DefaultConversionService(); + conversionService.addConverter(new MapConverter()); + Binder binder = new Binder(this.sources, null, conversionService); + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo", "boom"); + source.put("foo.a", "a"); + source.put("foo.b", "b"); + this.sources.add(source); + Map map = binder.bind("foo", STRING_STRING_MAP).get(); + assertThat(map.get("a")).isEqualTo("a"); + assertThat(map.get("b")).isEqualTo("b"); + } + @Test public void bindToMapWithNoConverterForValue() { MockConfigurationPropertySource source = new MockConfigurationPropertySource();