Spring-boot: YAML lists cannot be mapped to Sets

Created on 21 Aug 2014  路  14Comments  路  Source: spring-projects/spring-boot

I have a YAML application properties file and I'm trying to map a series of items to a Set. This appears to be impossible given the Spring Beans code that processes the properties, but the documentation tells a different story.

YAML:

test:
  - "value"

Code:

@ConfigurationProperties
public class MyTest {

    private Set<String> test;

    public void setTest(Set<String> test) {
        this.test = test;
    }

    public Set<String> getTest() {
        return this.test;
    }
}

The spring-boot documentation states:

To bind to properties like that using the Spring DataBinder utilities (which is what @ConfigurationProperties does) you need to have a property in the target bean of type java.util.List (or Set) and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above

However, Set is not supported. If you attempt to map a YAML property to a Set, you'll get an exception similar to the following:

Property referenced in indexed property path 'test[0]' is neither an array nor a List nor a Map; returned value was [value]

The documentation should be corrected on this point.

config-data bug

Most helpful comment

This is still happening in Spring Boot 1.4.0.M3. Trying to set property this way always fails:

security:
  filter-dispatcher-types:
    - REQUEST
    - ERROR

Only workaround I found is:

security:
  filter-dispatcher-types: REQUEST, ERROR

All 14 comments

There are tests working with Set in RelaxedDataBinderTests so it should work. Can you put a sample project that reproduces the issue. Ideally here? Thanks

Closing due to lack of response. Please comment again and we can reopen the issue if you're still having trouble.

It looks like the RelaxedDataBinderTests are testing that the YAML foo: bar,bat can get mapped to a Set, while this issue appears to be about the YAML:

foo:
-bar
-bat

I duplicated the issue in my Boot 1.2.5.RELEASE app and stumbled across this thread. Once I changed my Set objects back to List everything worked as expected. I don't really think it's a code issue as there is no guarantee of uniqueness in a YAML list, so trying to map them to a Set is incomplete at best. However, I do agree with wooder79 that the (or Set) language in the documentation implies that the two types are interchangeable and is misleading.

@dan-nawrocki Are you able to share the code that duplicated the issue to https://github.com/spring-projects/spring-boot-issues?

Sure. It looks like adding this to RelaxedDataBinderTests does the trick:

    @Test
    public void testBindNestedSetFromList() throws Exception {
        TargetWithNestedSet target = new TargetWithNestedSet();
        this.conversionService = new DefaultConversionService();
        bind(target, "nested[0]: bar");
        assertEquals("[bar]", target.getNested().toString());
    }

Could you also support below syntax? the object array.

test:
  foo:
    - name: abc
      value: 123
    - name: efg
      value: 456

This is still happening in Spring Boot 1.4.0.M3. Trying to set property this way always fails:

security:
  filter-dispatcher-types:
    - REQUEST
    - ERROR

Only workaround I found is:

security:
  filter-dispatcher-types: REQUEST, ERROR

@ulisesbocchio This issue is still open so it's to be expected that 1.4 behaves as you have described

@philwebb or @snicoll is there a reason this has to be pushed all the way to 2.0? This seems like a pretty simple bug fix that is impacting all 1.x users that declare Sets. Thanks!

@lhazlewood Unfortunately it's not so easy to fix with our current design.

Taking into account Set instances in the processKeyedProperty method of AbstractNestablePropertyAccessor in spring-beans project passes the following test (based on the @dan-nawrocki 's test in this comment):

    @Test
    public void testBindNestedSetFromList() throws Exception {
        TargetWithNestedSet target = new TargetWithNestedSet();
        this.conversionService = new DefaultConversionService();
        bind(target, "nested[0]: bar");
        bind(target, "nested[1]: foo");
        bind(target, "nested[2]: bar");
        assertThat(target.getNested().toString()).isEqualTo("[bar, foo]");
    }

@philwebb Do you think this problem must be managed here? If so, I can make a pull request to spring-framework project.

@antoinecarton We have plans to rework the binding system entirely. I'd suggest waiting until we get a bit further before suggesting any framework changes. Thanks!

try this taken from here

#YAML also has a set type, which looks like this:
set:
    ? item1
    ? item2
    ? item3

#Like Python, sets are just maps with null values; the above is equivalent to:
set2:
    item1: null
    item2: null
    item3: null

@philwebb Any news on this ?

Sorry for the interruption, that was done for Spring Boot 2.x

Was this page helpful?
0 / 5 - 0 ratings