Mapping list in Yaml to list of objects in Spring Boot
Solution 1
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?
Solution 2
- 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
}
}
Solution 3
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!
Solution 4
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 :)
Solution 5
I tried 2 solutions, both work.
Solution_1
.yml
available-users-list:
configurations:
-
username: eXvn817zDinHun2QLQ==
password: IP2qP+BQfWKJMVeY7Q==
-
username: uwJlOl/jP6/fZLMm0w==
password: IP2qP+BQKJLIMVeY7Q==
LoginInfos.java
@ConfigurationProperties(prefix = "available-users-list")
@Configuration
@Component
@Data
public class LoginInfos {
private List<LoginInfo> configurations;
@Data
public static class LoginInfo {
private String username;
private String password;
}
}
List<LoginInfos.LoginInfo> list = loginInfos.getConfigurations();
Solution_2
.yml
available-users-list: '[{"username":"eXvn817zHBVn2QLQ==","password":"IfWKJLIMVeY7Q=="}, {"username":"uwJlOl/g9jP6/0w==","password":"IP2qWKJLIMVeY7Q=="}]'
Java
@Value("${available-users-listt}")
String testList;
ObjectMapper mapper = new ObjectMapper();
LoginInfos.LoginInfo[] array = mapper.readValue(testList, LoginInfos.LoginInfo[].class);
Tomasz Dziurko
Java freelancer with over 5 yrs experience in web application development, blogger, Wicket fan
Updated on July 05, 2022Comments
-
Tomasz Dziurko almost 2 years
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?