How to write a Spring Boot YAML configuration with nested JSON? - spring

I have been using Spring Boot applications with YAML for the external configuration, and this has been working very well so far. A toy example:
#Component
#ConfigurationProperties
class MyConfig {
String aaa;
Foo foo;
static class Foo {
String bar;
}
}
And then a yaml file with the following properties:
aaa: hello
foo.bar: world
My problem is that I really need to add a JsonObject into my configuration. I first tried adding it as a field in the MyConfig class, and then writing the following YAML file which I believe is syntactically valid:
aaa: hello
from:
{
"first_initial": "D",
"last_initial": "E"
}
foo.bar: world
Spring threw the following error with that: Cannot access indexed value in property referenced...
I finally resorted to making the value a plain string instead and using the > folding tag to put it in the YAML, but this means I have to manually parse the string into a JsonObject in my code.
Anyone have an idea for how to do this?

This should work:
#Component
#ConfigurationProperties
class MyConfig {
String aaa;
Foo foo;
String from;
static class Foo {
String bar;
}
// ... getters setters
public JsonObject getFromAsJson() {
// create object from "this.from"
}
}
aaa: hello
foo:
bar: world
from: |
{
"first_initial": "D",
"last_initial": "E"
}
and this:
aaa: hello
foo:
bar: world
from: "{\"first_initial\": \"D\", \"last_initial\": \"E\"}"
The first version would preserve line breaks.

Related

Spring does not load Map of objects from YAML, if the key String contains dots [duplicate]

I have a following yml config:
foo:
bar.com:
a: b
baz.com:
a: c
With a following class Spring tries to inject map with keys 'bar' and 'baz', treating dots as separator:
public class JavaBean {
private Map<String, AnotherBean> foo;
(...)
}
I have tried quoting the key (i.e. 'bar.com' or "bar.com") but to no avail - still the same problem. Is there a way around this?
A slight revision of #fivetenwill's answer, which works for me on Spring Boot 1.4.3.RELEASE:
foo:
"[bar.com]":
a: b
"[baz.com]":
a: c
You need the brackets to be inside quotes, otherwise the YAML parser basically discards them before they get to Spring, and they don't make it into the property name.
This should work:
foo:
"[bar.com]":
a: b
"[baz.com]":
a: c
Inspired from Spring Boot Configuration Binding Wiki
This is not possible if you want automatic mapping of yaml keys to Java bean attributes. Reason being, Spring first convert YAML into properties format.
See section 24.6.1 of link below:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
So, your YAML is converted to:
foo.bar.com.a=b
foo.baz.com.a=c
Above keys are parsed as standard properties.
As a work around, you can use Spring's YamlMapFactoryBean to create a Yaml Map as it is. Then, you can use that map to create Java beans by your own.
#Configuration
public class Config {
private Map<String, Object> foo;
#Bean
public Map<String, Object> setup() {
foo = yamlFactory().getObject();
System.out.println(foo); //Prints {foo={bar.com={a=b}, baz.com={a=c}}}
return foo;
}
#Bean
public YamlMapFactoryBean yamlFactory() {
YamlMapFactoryBean factory = new YamlMapFactoryBean();
factory.setResources(resource());
return factory;
}
public Resource resource() {
return new ClassPathResource("a.yaml"); //a.yaml contains your yaml config in question
}
}

Spring configuration binding failes

I have following block in application.yaml file:
foo:
bar: bazz
I want to map that configuration to a configuration class using #ConfigurationProperties.
#Validated
#Getter #Setter
#ConfigurationProperties(prefix = "foo")
public class FooProperties {
#NotNull
private String bar;
}
And here is configuration class
#Configuration
#EnableConfigurationProperties(FooProperties.class)
public class FooConfiguration {
#Bean
public Foo getFoo(FooProperties properties) {
///
}
}
However when I try to start application I get following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'foo' to a.b.c.FooProperties failed:
Property: foo.bar
Value: null
Reason: must not be null
Action:
Update your application's configuration
Did I miss anything else? I cant figure out why such a trivial thing fails.
Try putting the value in single quotes like:
foo:
bar: 'bazz'
Since the value being read is mapped to a string variable.

Spring boot yml property file processing [duplicate]

I have a following yml config:
foo:
bar.com:
a: b
baz.com:
a: c
With a following class Spring tries to inject map with keys 'bar' and 'baz', treating dots as separator:
public class JavaBean {
private Map<String, AnotherBean> foo;
(...)
}
I have tried quoting the key (i.e. 'bar.com' or "bar.com") but to no avail - still the same problem. Is there a way around this?
A slight revision of #fivetenwill's answer, which works for me on Spring Boot 1.4.3.RELEASE:
foo:
"[bar.com]":
a: b
"[baz.com]":
a: c
You need the brackets to be inside quotes, otherwise the YAML parser basically discards them before they get to Spring, and they don't make it into the property name.
This should work:
foo:
"[bar.com]":
a: b
"[baz.com]":
a: c
Inspired from Spring Boot Configuration Binding Wiki
This is not possible if you want automatic mapping of yaml keys to Java bean attributes. Reason being, Spring first convert YAML into properties format.
See section 24.6.1 of link below:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
So, your YAML is converted to:
foo.bar.com.a=b
foo.baz.com.a=c
Above keys are parsed as standard properties.
As a work around, you can use Spring's YamlMapFactoryBean to create a Yaml Map as it is. Then, you can use that map to create Java beans by your own.
#Configuration
public class Config {
private Map<String, Object> foo;
#Bean
public Map<String, Object> setup() {
foo = yamlFactory().getObject();
System.out.println(foo); //Prints {foo={bar.com={a=b}, baz.com={a=c}}}
return foo;
}
#Bean
public YamlMapFactoryBean yamlFactory() {
YamlMapFactoryBean factory = new YamlMapFactoryBean();
factory.setResources(resource());
return factory;
}
public Resource resource() {
return new ClassPathResource("a.yaml"); //a.yaml contains your yaml config in question
}
}

Escaping a dot in a Map key in Yaml in Spring Boot

I have a following yml config:
foo:
bar.com:
a: b
baz.com:
a: c
With a following class Spring tries to inject map with keys 'bar' and 'baz', treating dots as separator:
public class JavaBean {
private Map<String, AnotherBean> foo;
(...)
}
I have tried quoting the key (i.e. 'bar.com' or "bar.com") but to no avail - still the same problem. Is there a way around this?
A slight revision of #fivetenwill's answer, which works for me on Spring Boot 1.4.3.RELEASE:
foo:
"[bar.com]":
a: b
"[baz.com]":
a: c
You need the brackets to be inside quotes, otherwise the YAML parser basically discards them before they get to Spring, and they don't make it into the property name.
This should work:
foo:
"[bar.com]":
a: b
"[baz.com]":
a: c
Inspired from Spring Boot Configuration Binding Wiki
This is not possible if you want automatic mapping of yaml keys to Java bean attributes. Reason being, Spring first convert YAML into properties format.
See section 24.6.1 of link below:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
So, your YAML is converted to:
foo.bar.com.a=b
foo.baz.com.a=c
Above keys are parsed as standard properties.
As a work around, you can use Spring's YamlMapFactoryBean to create a Yaml Map as it is. Then, you can use that map to create Java beans by your own.
#Configuration
public class Config {
private Map<String, Object> foo;
#Bean
public Map<String, Object> setup() {
foo = yamlFactory().getObject();
System.out.println(foo); //Prints {foo={bar.com={a=b}, baz.com={a=c}}}
return foo;
}
#Bean
public YamlMapFactoryBean yamlFactory() {
YamlMapFactoryBean factory = new YamlMapFactoryBean();
factory.setResources(resource());
return factory;
}
public Resource resource() {
return new ClassPathResource("a.yaml"); //a.yaml contains your yaml config in question
}
}

Groovy Spring DSL: constructing beans with properties of other beans

I have a bunch of Spring beans some of which need to be initialized from other beans, and some of which need to be initialized from properties of those other beans. E.g.:
Foo {
}
Bar {
String getBaz()
}
Qux {
Qux(Foo foo, String baz)
}
I thought I could write something like
beans = {
foo(Foo) {}
bar(Bar) {}
qux(Qux, ref('foo'), ref('bar').baz) {}
}
but obviously this doesn't work because ref('bar') isn't Bar, it's a RuntimeBeanReference.
In plain Spring (3+) what I want is apparently possible with spring expressions but I can't figure out the necessary syntax with the Grails Spring DSL. Can it be done?
I think you meant the classes to look like this:
class Foo {
}
class Bar {
String baz
}
class Qux {
Foo foo
String baz
Qux(Foo f, String b) {
foo = f
baz = b
}
}
and the 2nd ref('foo') should have been ref('bar'). Then this will work:
beans = {
foo(Foo)
bar(Bar) {
baz = 'wazzup'
}
qux(Qux, ref('foo'), '#{bar.baz}')
}

Resources