Only use Map converter binding when no descendants

Update `MapBinder` to only attempt conversion based binding when there
are no know descendant elements.

See gh-11892
This commit is contained in:
Phillip Webb 2018-02-02 13:48:27 -08:00
parent d6ae4e48d8
commit 16b7dbf487
2 changed files with 20 additions and 1 deletions

View File

@ -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<Object, Object>> {
Map<Object, Object> 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())

View File

@ -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<String, String> 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();