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