Mapping list in Yaml to list of objects in Spring Boot

In my Spring Boot app I have application.yaml configuration file with following content. I want to have it injected as a Configuration object with list of channel configurations:

available-payment-channels-list:
  xyz: "123"
  channelConfigurations:
    -
      name: "Company X"
      companyBankAccount: "1000200030004000"
    -
      name: "Company Y"
      companyBankAccount: "1000200030004000"

And @Configuration object I want to be populated with list of PaymentConfiguration objects:

    @ConfigurationProperties(prefix = "available-payment-channels-list")
    @Configuration
    @RefreshScope
    public class AvailableChannelsConfiguration {

        private String xyz;

        private List<ChannelConfiguration> channelConfigurations;

        public AvailableChannelsConfiguration(String xyz, List<ChannelConfiguration> channelConfigurations) {
            this.xyz = xyz;
            this.channelConfigurations = channelConfigurations;
        }

        public AvailableChannelsConfiguration() {

        }

        // getters, setters


        @ConfigurationProperties(prefix = "available-payment-channels-list.channelConfigurations")
        @Configuration
        public static class ChannelConfiguration {
            private String name;
            private String companyBankAccount;

            public ChannelConfiguration(String name, String companyBankAccount) {
                this.name = name;
                this.companyBankAccount = companyBankAccount;
            }

            public ChannelConfiguration() {
            }

            // getters, setters
        }

    }

I am injecting this as a normal bean with @Autowired constructor. Value of xyz is populated correctly, but when Spring tries to parse yaml into list of objects I am getting

   nested exception is java.lang.IllegalStateException: 
    Cannot convert value of type [java.lang.String] to required type    
    [io.example.AvailableChannelsConfiguration$ChannelConfiguration] 
    for property 'channelConfigurations[0]': no matching editors or 
    conversion strategy found]

Any clues what is wrong here?

Answers


The reason must be somewhere else. Using only Spring Boot 1.2.2 out of the box with no configuration, it Just Works. Have a look at this repo - can you get it to break?

https://github.com/konrad-garus/so-yaml

Are you sure the YAML file looks exactly the way you pasted? No extra whitespace, characters, special characters, mis-indentation or something of that sort? Is it possible you have another file elsewhere in the search path that is used instead of the one you're expecting?


  • You don't need constructors
  • You don't need to annotate inner classes
  • RefreshScope have some problems when using with @Configuration. Please see this github issue

Change your class like this:

@ConfigurationProperties(prefix = "available-payment-channels-list")
@Configuration
public class AvailableChannelsConfiguration {

private String xyz;
private List<ChannelConfiguration> channelConfigurations;

// getters, setters

public static class ChannelConfiguration {
    private String name;
    private String companyBankAccount;

    // getters, setters
}

}

I had referenced this article and many others and did not find a clear cut concise response to help. I am offering my discovery, arrived at with some references from this thread, in the following:

Spring-Boot version: 1.3.5.RELEASE

Spring-Core version: 4.2.6.RELEASE

Dependency Management: Brixton.SR1

The following is the pertinent yaml excerpt:

tools:
  toolList:
    - 
      name: jira
      matchUrl: http://someJiraUrl
    - 
      name: bamboo
      matchUrl: http://someBambooUrl

I created a Tools.class:

@Component
@ConfigurationProperties(prefix = "tools")
public class Tools{
    private List<Tool> toolList = new ArrayList<>();
    public Tools(){
      //empty ctor
    }

    public List<Tool> getToolList(){
        return toolList;
    }

    public void setToolList(List<Tool> tools){
       this.toolList = tools;
    }
}

I created a Tool.class:

@Component
public class Tool{
    private String name;
    private String matchUrl;

    public Tool(){
      //empty ctor
    }

    public String getName(){
        return name;
    }

    public void setName(String name){
       this.name= name;
    }
    public String getMatchUrl(){
        return matchUrl;
    }

    public void setMatchUrl(String matchUrl){
       this.matchUrl= matchUrl;
    }

    @Override
    public String toString(){
        StringBuffer sb = new StringBuffer();
        String ls = System.lineSeparator();
        sb.append(ls);
        sb.append("name:  " + name);
        sb.append(ls);
        sb.append("matchUrl:  " + matchUrl);
        sb.append(ls);
    }
}

I used this combination in another class through @Autowired

@Component
public class SomeOtherClass{

   private Logger logger = LoggerFactory.getLogger(SomeOtherClass.class);

   @Autowired
   private Tools tools;

   /* excluded non-related code */

   @PostConstruct
   private void init(){
       List<Tool>  toolList = tools.getToolList();
       if(toolList.size() > 0){
           for(Tool t: toolList){
               logger.info(t.toString());
           }
       }else{
           logger.info("*****-----     tool size is zero     -----*****");
       }  
   }

   /* excluded non-related code */

}

And in my logs the name and matching url's were logged. This was developed on another machine and thus I had to retype all of the above so please forgive me in advance if I inadvertently mistyped.

I hope this consolidation comment is helpful to many and I thank the previous contributors to this thread!


I had much issues with this one too. I finally found out what's the final deal.

Referring to @Gokhan Oner answer, once you've got your Service class and the POJO representing your object, your YAML config file nice and lean, if you use the annotation @ConfigurationProperties, you have to explicitly get the object for being able to use it. Like :

@ConfigurationProperties(prefix = "available-payment-channels-list")
//@Configuration  <-  you don't specificly need this, instead you're doing something else
public class AvailableChannelsConfiguration {

    private String xyz;
    //initialize arraylist
    private List<ChannelConfiguration> channelConfigurations = new ArrayList<>();

    public AvailableChannelsConfiguration() {
        for(ChannelConfiguration current : this.getChannelConfigurations()) {
            System.out.println(current.getName()); //TADAAA
        }
    }

    public List<ChannelConfiguration> getChannelConfigurations() {
        return this.channelConfigurations;
    }

    public static class ChannelConfiguration {
        private String name;
        private String companyBankAccount;
    }

}

And then here you go. It's simple as hell, but we have to know that we must call the object getter. I was waiting at initialization, wishing the object was being built with the value but no. Hope it helps :)


for me the fix was to add the injected class as inner class in the one annotated with @ConfigurationProperites, because I think you need @Component to inject properties.


Need Your Help

Display current time in 12 hour format with AM/PM

java date-format

Currently the time displayed as 13:35 PM

Keras accuracy does not change

python audio machine-learning theano keras

I have a few thousand audio files and I want to classify them using Keras and Theano. So far, I generated a 28x28 spectrograms (bigger is probably better, but I am just trying to get the algorithm ...