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<RichType> 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
pull/13161/head
Andy Wilkinson 7 years ago
parent 45f4e8795e
commit 3992dacdc8

@ -18,6 +18,7 @@ package org.springframework.boot.context.properties.bind;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.function.Supplier; 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.core.ResolvableType;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/** /**
* Base class for {@link AggregateBinder AggregateBinders} that read a sequential run of * Base class for {@link AggregateBinder AggregateBinders} that read a sequential run of
@ -81,11 +83,17 @@ abstract class IndexedElementsBinder<T> extends AggregateBinder<T> {
ResolvableType aggregateType, ResolvableType elementType) { ResolvableType aggregateType, ResolvableType elementType) {
ConfigurationProperty property = source.getConfigurationProperty(root); ConfigurationProperty property = source.getConfigurationProperty(root);
if (property != null) { if (property != null) {
Object aggregate = convert(property.getValue(), aggregateType, Collection<Object> elements;
target.getAnnotations()); Object value = property.getValue();
ResolvableType collectionType = ResolvableType if (value instanceof String && !StringUtils.hasText((String) value)) {
.forClassWithGenerics(collection.get().getClass(), elementType); elements = Collections.emptyList();
Collection<Object> elements = convert(aggregate, collectionType); }
else {
Object aggregate = convert(value, aggregateType, target.getAnnotations());
ResolvableType collectionType = ResolvableType
.forClassWithGenerics(collection.get().getClass(), elementType);
elements = convert(aggregate, collectionType);
}
collection.get().addAll(elements); collection.get().addAll(elements);
} }
else { else {

@ -363,6 +363,19 @@ public class CollectionBinderTests {
assertThat(foo.getFoos().get(1).getValue()).isEqualTo("three"); 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<BeanWithNestedCollection> target = Bindable
.of(BeanWithNestedCollection.class);
BeanWithNestedCollection foo = this.binder.bind("foo", target).get();
assertThat(foo.getValue()).isEqualTo("one");
assertThat(foo.getFoos()).isEmpty();
}
@Test @Test
public void bindToCollectionShouldUsePropertyEditor() { public void bindToCollectionShouldUsePropertyEditor() {
// gh-12166 // gh-12166

Loading…
Cancel
Save