theprogrammer
theprogrammer

Reputation: 2019

How to get nested Spel expression based objects through @ConfigurationProperties in Spring Boot?

This is in my application.yml

translations:
  en:
    title: 'english'
    description: 'english text'
  fr:
    title: 'french'
    description: 'french text'
  de:
    title: 'german'
    description: 'french text'

codes:
  GB:
    # I am assuming that en here will have an object with both title and description properties
    en: ${translations.en}
  CA:
    en: ${translations.en}
    fr: ${translations.fr}
  DE:
    en: ${translations.en}
    fr: ${translations.fr}
    de: ${translations.de}

Now consider this piece of code(removed unnecessary getters etc and also using project lombok for brevity)

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
    // Inner classes, but these can be outside as well
    @ConfigurationProperties
    @Getter
    @Component
    class Config {
        Map<String, Map<String, LanguageDTO>> codes;
    }

    @Data
    @AllArgsConstructor
    class LanguageDTO {
        String title;
        String description;
    }

    @Autowired
    Config config;

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

    @Override
    public void run(String... args) throws Exception {
        // Getting error on this line below
        Map<String, Map<String, LanguageDTO>> codes = config.getCodes();
    }
}

Now when I start the app, I am getting an error like this:

APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'codes.gb.en' to com.example.demo.DemoApplication$LanguageDTO:

    Property: codes.gb.en
    Value: ${translations.en}
    Origin: class path resource [application.yml]:24:9
    Reason: No converter found capable of converting from type [java.lang.String] to type [com.example.demo.DemoApplication$LanguageDTO]

Action:

Update your application's configuration

What I want?

I want to be able to read codes as a Map<String, Map<String, LanguageDTO>. That is, I should be able to do config.getCodes().get("GB").get("en") --> which should in turn have a data type of LanguageDTO.

Upvotes: 2

Views: 732

Answers (1)

Mafor
Mafor

Reputation: 10681

I don't think it's doable: Spring supports only simple property placeholders inside the application.yaml file (as far as I know). What you could do though, is leveraging YAML format built-in functionality, anchors and aliases:

translations:
  en: &en
    title: 'english'
    description: 'english text'
  fr: &fr
    title: 'french'
    description: 'french text'
  de: &de
    title: 'german'
    description: 'french text'

codes:
  GB:
    en: *en
  CA:
    en: *en
    fr: *fr
  DE:
    en: *en
    fr: *fr
    de: *de

For it to work, the LanguageDTO class needs to have setters and a default constructor. @AllArgsConstructor won't work. Alternatively you might try to get it work in conjunction with constructor binding, although I'm not sure if it's possible.

Upvotes: 1

Related Questions