Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot read array from YAML (properties) file

This is my project structure

- src
    - main
        - java
            - mypackage
            - resources
                - config
                    application.yml

and i have this in application.yml

document:
    templates:
        filetypes:
            - elem1
            - elem2
            - elem3
            - elem4
    hello:
        test: "hello"

in my Endpoint i have the following

@Value("${document.templates.filetypes}")
List<String> templatesFileTypes;

@Value("${document.hello.test}")
String hello;

in any function i can access something like System.out.println(hello) and its perfectly working but for the fileTypes its not even compiling and i receive this error :

Error creating bean with name 'configurationEndPoint': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'document.templates.filetypes' in value "${document.templates.filetypes}"

Searched alot and every solution i could find was that pointing to the write application.yml/application.xml file, which is invalid in my case since i can read the other test String but not the array;

i tried String[] i tried ArrayList<String> but i can't get it to work

like image 795
Yamen Nassif Avatar asked May 17 '18 16:05

Yamen Nassif


2 Answers

One way is to pass the elements as delimited list. Typically we have used comma and it works out of the box for String arrays. To use a List, then you will need to set the delimiter using Spring SPEL format... see example below.

document:
    templates:
        filetypes:elem1,elem2,elem3

-

@Value("${document.templates.filetypes:}")
private String[] array;


@Value("#{'${document.templates.filetypes}'.split(',')}")
private List<String> list;

@PostConstruct
void testList(){
    list.stream().forEach(System.out::println);
    for (String a : array) {
        System.out.println(a);
    }
}
like image 186
Jose Martinez Avatar answered Sep 22 '22 09:09

Jose Martinez


The other solution provided by @Jose Martinez would work but not really as clear as needed, because its reading document.templates.filetypes as a String and then splitting it into array of Strings; thus i am adding my solution to this,

1- Create new class FileTypesProperties

@Configuration
@ConfigurationProperties(prefix = "document.templates")
public class FileTypesConfig {

    private List<String> fileTypes;

    public List<String> getFileTypes() {
        return fileTypes;
    }

    public void setFileTypes(List<String> fileTypes) {
        this.fileTypes = fileTypes;
    }
}

2- Create service and inject the previous class

@Service
public class FileTypeService {
    private final List<String> fileTypes;

    @Autowired
    public FileTypeService(FileTypesConfig fileTypesConfig){
        this.fileTypes = fileTypesConfig.getFileTypes();
    }

    public List<String> getFileTypes(){
        return this.fileTypes;
    }

}

3- In your end point simply autowire and call the previous service

@RestController
public class ConfigurationEndPoint {

    @Autowired
    FileTypeService fileTypeService;
    @GetMapping("/api/filetypes")
    @ResponseBody
    public ResponseEntity<List<String>> getDocumentTemplatesFileTypes(){
        return ResponseEntity.ok(fileTypeService.getFileTypes());
    }
}

And then your yaml file can be a real array

document:
    templates:
        file-types:
            - elem1
            - elem2
            - elem3
            - elem4

I believe this is cleaner than splitting a String into smaller strings into an array, hope this will help out someone.

like image 43
Yamen Nassif Avatar answered Sep 22 '22 09:09

Yamen Nassif