1

I have a declaration in a POJO that looks something like this:

@Value("${prefix.someList}")
private List<String> list;

I have a line in a properties file that looks like this:

prefix.list   = some,stuff,with,comma,separators

I also added a @PostConstruct method to the POJO, only so I could add a line I could set a breakpoint on.

When I hit the breakpoint, I look at the "list" property, and it has a SINGLE entry with the value "some,stuff,with,comma,separators".

I also added the following declaration:

@Value("${prefix.someList}")
private String[] array;

At the breakpoint, the array had 5 entries, with the expected values.

If I convert the first expression to

"#{'${prefix.list}'.split(',')}"

Then it does produce a list with the expected entries, but I'd prefer not to do that. It's a little more complicated, and doesn't defend against empty list properties.

I then did some googling for this problem. I saw suggestions that I should add a "ConversionService" bean, instantiated with "new DefaultConversionService()". I did this. I also discovered the references in there for "StringToCollectionConverter" and "StringToArrayConverter". I set breakpoints in expected places in these classes.

I found the DefaultConversionService being created, along with the two converter classes. However, the "convert" methods in those classes was never called, even the "array" one, even though something was doing the conversion.

I then even commented out my Bean that creates the DefaultConversionService, and I found the result was identical (I noticed the constructor was being hit twice).

So, in short, conversion of string to list is not happening, even though string to array is, but I have no idea what mechanism is supposed to be doing this, because neither of the expected converters seem to even be used, even though the array conversion is being done somewhere.

I'm using Spring 5.0.9 with Java 8.

To be clear, the other post that is allegedly a duplicate of this does not help. First, the "answer" is simply a suboptimal workaround that really shouldn't be necessary. The other suggestion in that post didn't make any difference.

Update:

It was suggested that I should construct a minimal reproducible example. Fortunately, start.spring.io makes it simple to produce a Spring boot skeleton (I didn't mention before that this is a Spring Boot app).

I took the sample maven and java8 project. Note that I selected version 2.1.0, but my own project is using 2.0.5.

I added a dummy class, added a @Component annotation, added a list and array property, a @PostConstruct method that prints the list and length of the list, and defined a csv list property in application.properties.

I ran it. It did what you said it would. It worked fine, producing a list with as many elements as there were in the csv list. It was curious, however, that it still doesn't appear to be calling the converters in the conversion service. None of the breakpoints in the converter classes were hit.

Then, I changed the spring boot version in the pom from 2.1.0 to 2.0.5 and reran my test. Boom. I got exactly the behavior I've been seeing. it produced a list with one element, where the string was the entire csv list.

So, it appears that something was changed or fixed between 2.0.5 and 2.1.0 that made this work properly.

I can't upgrade right now, so I can't use this solution. I would search through the spring-boot change log, but as I never saw it hit the expected converter classes, I wonder what is actually doing this.

Update:

If it matters, here's my simple pojo that I added to the springboot skeleton:

package com.example.demo;

import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Stuff {
    @Value("${stuffList}")
    private List<String>    stuffList;
    @Value("${stuffList}")
    private String[]    stuffArray;

    public final List<String> getStuffList() { return stuffList; }

    public final void setStuffList(List<String> stuffList) { this.stuffList = stuffList; }

    public final String[] getStuffArray() { return stuffArray; }

    public final void setStuffArray(String[] stuffArray) { this.stuffArray = stuffArray; }

    @PostConstruct
    public void stuff() {
        System.out.println("stuffList[" + stuffList + "] len[" + stuffList.size() + "]");
    }
}

With Springboot 2.1.0, the len is 3. With 2.0.5, the len is 1. Neither of them call "`org.springframework.core.convert.support.StringToCollectionConverter.convert(Object, TypeDescriptor, TypeDescriptor)'"

And this is the main class, unmodified from the skeleton.

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

Update:

Ok, the solution is described at Reading a List from properties file and load with spring annotation @Value , but it's not the accepted answer. The correct answer is the one that describes defining the bean of type ConversionService with an object of type DefaultConversionService.

What can be confusing is that if you set some breakpoints in DefaultConversionService before adding that bean, you will hit them. However, it appears that Spring uses that class by default for other things besides property value resolution. Once I added the bean (and I did it in the correct class), that got the StringToCollection converter to work properly.

David M. Karr
  • 14,317
  • 20
  • 94
  • 199
  • Possible duplicate of [Reading a List from properties file and load with spring annotation @Value](https://stackoverflow.com/questions/12576156/reading-a-list-from-properties-file-and-load-with-spring-annotation-value) – Savior Nov 11 '19 at 21:16
  • [This answer](https://stackoverflow.com/a/29970335/5191913) worked for me. Please provide a [mcve] if it doesn't for you. – Savior Nov 11 '19 at 21:16
  • I also saw the post both of you note. In my post, I show that I already tried the accepted answer, along with the other answer that seemed more appropriate. The accepted answer it provides I already indicated is sub-optimal, as it is more verbose and doesn't seem like it should be necessary. The other suggestion didn't make any difference (as it was already instantiating that bean anyway). – David M. Karr Nov 11 '19 at 21:32
  • I think you must be doing something else wrong. Please provide a [mcve]. – Savior Nov 11 '19 at 21:35
  • 1
    Does this answer your question? [How to split string in Thymeleaf](https://stackoverflow.com/questions/35669169/how-to-split-string-in-thymeleaf) using #strings.arraySplit – Sully Nov 11 '19 at 21:53
  • @HithamS.AlQadheeb This has nothing to do with Thymeleaf. They're trying to parse entries in a properties file, not render an HTML template. – Savior Nov 11 '19 at 22:04
  • Well, I produced a minimal example from start.spring.io, but it doesn't demonstrate the problem. It's curious that it STILL doesn't hit the convert methods. – David M. Karr Nov 11 '19 at 22:09
  • With the `DefaultConversionService`, I hit the breakpoint in `StringToCollectionConverter#convert`. – Savior Nov 11 '19 at 22:13
  • I don't understand that. I took the simple skeleton from start.spring.io and just added my simple pojo with @Component and the list variable, along with the csv list property, and it never hit that method. However, as I describe in the update, downgrading the skeleton from springboot 2.1.0 to 2.0.5 demonstrated my problem. – David M. Karr Nov 11 '19 at 22:28
  • Even with Spring Boot 2.0.5 which uses Spring `5.0.9.RELEASE`, this works for me. What does your `@Configuration` or `SpringApplication` look like? – Savior Nov 11 '19 at 22:34
  • I don't have a @Configuration annotation. The only thing I changed from the generated skeleton was adding the pojo as described above and changing the version of the parent to 2.0.5. – David M. Karr Nov 11 '19 at 22:42
  • Where's the `DefaultConversionService` bean? – Savior Nov 11 '19 at 22:48
  • Something is defining it implicitly. It hits other breakpoints in DCS, like the line that adds the StringToCollectionConverter to the converter list, it just never calls the convert method. In any case, when I had the explicit bean to define it, it behaved exactly the same. – David M. Karr Nov 11 '19 at 22:52
  • It does seem like Spring Boot creates `DefaultConversionService` objects, but not for property resolution. With an explicit `@Bean` declaration in the `DemoApplication`, I get the correct behavior, with 5 elements in the list. – Savior Nov 11 '19 at 22:56
  • Ok. I confirm that this fixes the skeleton, but this didn't fix my real code. Let me try that again. – David M. Karr Nov 11 '19 at 23:04
  • Ok. I confirm that this fixes the skeleton, but this didn't fix my real code. Let me try that again. After checking, I discovered that I put the Bean in a class in a different project. I now confirm that adding the Bean fixes the problem, no matter whether in 2.0.5 or 2.1.0. – David M. Karr Nov 11 '19 at 23:48

0 Answers0