From 3992dacdc803aa573a617f38d3349aa42aaac79d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 11 May 2018 16:41:37 +0100 Subject: [PATCH] Bind empty string to empty collection or array of rich types Previously, when an empty String was bound to a collection or array of rich types it would fail as there was no converter capable of creating a Collection or RichType[] from the String. This commit updates IndexedElementsBinder to apply special treatment to empty String values. Now, when such a value is being processed, an empty Collection or array is the result. Closes gh-12965 --- .../properties/bind/IndexedElementsBinder.java | 18 +++++++++++++----- .../properties/bind/CollectionBinderTests.java | 13 +++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java index d07705c12d..acb863437b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java @@ -18,6 +18,7 @@ package org.springframework.boot.context.properties.bind; import java.lang.annotation.Annotation; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.TreeSet; import java.util.function.Supplier; @@ -32,6 +33,7 @@ import org.springframework.boot.context.properties.source.IterableConfigurationP import org.springframework.core.ResolvableType; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; /** * Base class for {@link AggregateBinder AggregateBinders} that read a sequential run of @@ -81,11 +83,17 @@ abstract class IndexedElementsBinder extends AggregateBinder { ResolvableType aggregateType, ResolvableType elementType) { ConfigurationProperty property = source.getConfigurationProperty(root); if (property != null) { - Object aggregate = convert(property.getValue(), aggregateType, - target.getAnnotations()); - ResolvableType collectionType = ResolvableType - .forClassWithGenerics(collection.get().getClass(), elementType); - Collection elements = convert(aggregate, collectionType); + Collection elements; + Object value = property.getValue(); + if (value instanceof String && !StringUtils.hasText((String) value)) { + elements = Collections.emptyList(); + } + else { + Object aggregate = convert(value, aggregateType, target.getAnnotations()); + ResolvableType collectionType = ResolvableType + .forClassWithGenerics(collection.get().getClass(), elementType); + elements = convert(aggregate, collectionType); + } collection.get().addAll(elements); } else { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java index 575009e313..da407edb73 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java @@ -363,6 +363,19 @@ public class CollectionBinderTests { assertThat(foo.getFoos().get(1).getValue()).isEqualTo("three"); } + @Test + public void bindToNestedCollectionWhenEmptyStringShouldReturnEmptyCollection() { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.value", "one"); + source.put("foo.foos", ""); + this.sources.add(source); + Bindable target = Bindable + .of(BeanWithNestedCollection.class); + BeanWithNestedCollection foo = this.binder.bind("foo", target).get(); + assertThat(foo.getValue()).isEqualTo("one"); + assertThat(foo.getFoos()).isEmpty(); + } + @Test public void bindToCollectionShouldUsePropertyEditor() { // gh-12166