0

I am trying to read a list of configuration items from application.yaml into a configuration bean via @ConfigurationProperties. The list may consist of objects with a defined base type:

@ConfigurationProperties("myapp")
public class MyConfiguration {
   private Set<Point> points;
}

public abstract class Point {
  private String name;
}

public class QuantityPoint extends Point {
  private Number value;
}

public class StatePoint extends Point {
  private String state;
}

Here an example application.yaml as I imagine it:

myapp:
  points:
  - name: foo
    value: 5
  - name: bar
    state: bar

I have found some posts which I thought are solving this exact question, but nothing suggested there has worked so far. I tried playing with ConfigurationProperties("myapp.points") on the subtypes on Point but as said: nothing has worked.

To be honest, I wonder how the parser should be able to distinguish the types. There must be something like a type argument missing similar to how we convert entities to json with Jackson.

At the moment, I am getting instances of Point (when I remove the abstract modifier) and the fields value and state are lost.

EDIT

I think I made some progress in finding out what needs to be done. Apparently, Spring is using SnakeYaml to parse the file and that library uses yaml tags to indicate type information so that my yaml should look something like

myapp:
  points:
  - !!foo
    name: foo
    value: 5
  - !!bar
    name: bar
    state: bar

There are some builtin tags but I would need to register my own. I found information how to write a custom Constructor by extending AbstractConstruct but I couldn't find anything on how to register this with the instance of Yaml that's set up by springboot.

Am I at least on the right track here?

EDIT

I noticed that the ConstructorException from snakeyaml comes so early in the startup process, it's probably not possible to to add my SafeConstructor to the process:

@Component
public class FooConstructor extends SafeConstructor {

    public FooConstructor() {
        this.yamlConstructors.put(new Tag("!foo"), new ConstructFoo());
    }
    
    private class ConstructFoo extends AbstractConstruct {
        @Override
        public Object construct(Node node) {
            String val = (String) constructScalar((ScalarNode) node);
            System.out.println(val);
            return null;
        }
    }
}
06:48:29.380 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed
org.yaml.snakeyaml.constructor.ConstructorException: could not determine a constructor for the tag !state
 in 'reader', line 5, column 5:
      - !foo
user3235738
  • 335
  • 4
  • 22
  • https://www.baeldung.com/configuration-properties-in-spring-boot#3-custom-converter – crizzis May 19 '21 at 17:47
  • The example in your link converts the string "john,2000" to an Employee object. That's not my scenario. – user3235738 May 19 '21 at 18:04
  • You scenario is to convert a `Map` to a `Point`. It's not very different – crizzis May 19 '21 at 18:08
  • Hmm that's a good point. Haven't thought of it that way. I'll give it a try, thanks! – user3235738 May 19 '21 at 18:09
  • The `Converter, Point>` is never called – user3235738 May 19 '21 at 18:18
  • Have you checked with adding the constructor for `QuantityPoint` and `StatePoint` – msucil May 20 '21 at 05:05
  • As far as I understand, the Constructor must be added to the `Yaml` object but I don't see how to get my hands on the one used to read the application.yml – user3235738 May 20 '21 at 10:58
  • I suspect that trying to make SnakeYAML do the conversion is going to throw off Spring's autoconfiguration anyway, as Spring wants to control the conversion from a tree of nested Lists/Maps to POJO by itself. Also, Spring is built to accept .properties files where it can accept a .yaml, so I wouldn't expect Spring to be built to exploit any SnakeYaml feature that isn't available in .properties files. Even if it worked, a future version of Spring might break that approach. – toolforger Apr 29 '22 at 07:07
  • Does this answer your question? [Spring Boot YML Config Class Inheritance](https://stackoverflow.com/questions/40367935/spring-boot-yml-config-class-inheritance) – Campi Oct 11 '22 at 19:50

0 Answers0