In my Spring Boot project I use a default Jackson ObjectMapper. I'd like to add new ObjectMapper to the Spring Context and start using it at new places, but also keep the default one. Adding new @Bean definition will override the default ObjectMapper. How can I add new ObjectMapper Bean without overriding the former one?
CodePudding user response:
So with a (generic, e.g. java.util.Map based) thing like:
class MyWrapper<K, V> {
final Map<K, V> map;
public MyWrapper(Map<K, V> map) {
this.map = map;
}
public Map<K, V> getMap() {
return map;
}
}
We can go wire it:
@Bean
MyWrapper<String, ObjectMapper> myStr2OMWrapper(/*ObjectMapper jacksonOm*/) {
return new MyWrapper() {
{
// map.put(DEFAULT, jacksonOm);
getMap().put("foo", fooMapper());
getMap().put("bar", barMapper());
}
};
}
..where fooMapper()
and barMapper()
can refer to (static/instance) no-bean methods:
private static ObjectMapper fooMapper() {
return new ObjectMapper()
.configure(SerializationFeature.INDENT_OUTPUT, true) // just a demo...
.configure(SerializationFeature.WRAP_ROOT_VALUE, true); // configure/set as see fit...
}
private static ObjectMapper barMapper() {
return new ObjectMapper()
.configure(SerializationFeature.INDENT_OUTPUT, false) // just a demo...
.configure(SerializationFeature.WRAP_ROOT_VALUE, false); // configure/set more...
}
(Already) testing/using time:
package com.example.demo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoAppTests {
@Autowired
MyWrapper<String, ObjectMapper> my;
@Autowired
ObjectMapper jacksonOM;
@Test
void contextLoads() {
System.err.println(jacksonOM);
Assertions.assertNotNull(jacksonOM);
my.getMap().entrySet().forEach(e -> {
System.err.println(e);
Assertions.assertNotNull(e.getValue());
});
}
}
Prints (e.g.)
...
com.fasterxml.jackson.databind.ObjectMapper@481b2f10
bar=com.fasterxml.jackson.databind.ObjectMapper@577bf0aa
foo=com.fasterxml.jackson.databind.ObjectMapper@7455dacb
...
Results:
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...
Sorry this test dosn't verify (individual) configuration, but only: (visually different) not null object mappers.
How to enable (multiple!) my.custom.jackson.*
auto configuration, is a more complex question... (it is not as easy as e.g. my.custom.datasource.*
config ;(
With:
@Bean
@Primary // ! for auto config, we need one primary (whether it is "spring.jackson" ... adjust;)
@ConfigurationProperties("spring.jackson")
JacksonProperties springJacksonProps() {
return new JacksonProperties();
}
@Bean
@ConfigurationProperties("foo.jackson")
JacksonProperties fooProps() {
return new JacksonProperties();
}
@Bean
@ConfigurationProperties("bar.jackson")
JacksonProperties barProps() {
return new JacksonProperties();
}
we can already load and differentiate (full blown) config like:
spring.jackson.locale=en_US
spring.jackson.time-zone=UTC
# ... all of spring.jackson @see org.springframework.boot.autoconfigure.jackson.JacksonProperties
foo.jackson.locale=en_US
foo.jackson.time-zone=PST
# ... just for demo purpose
bar.jackson.locale=de_DE
bar.jackson.time-zone=GMT 1
And also (no problem) pass them (props) to the according (static [foo|bar]Mapper
) methods.... but then?
Unfortunately the according("state of the art") code(to wire JacksonProperties
with "om builder") is not public (i.e. not extendable/pluggable).
Instead the auto configuration provides (if non defined/@ConditionalOnMissingBean
):
- a prototype
Jackson2ObjectMapperBuilder
bean, which (everytime when requested):- applies (i.e. receives) customization from all (known)
Jackson2ObjectMapperBuilderCustomizer
beans. - of which one (auto configured, order(0), package private) is the "standard" responsible for wiring
JacksonProperties
toJackson2ObjectMapperBuilder
...
- applies (i.e. receives) customization from all (known)
So the the simplest approach seems (up-to-date) to:
- steel/adopt the code (not-/implementing
Jackson2ObjectMapperBuilderCustomizer
) - construct (from properties) according builders/mappers, as see fit.
CodePudding user response:
If you want just a default ObjectMapper to use, I wrote a small utility that has some static methods for serializing/deserializing JSON and it uses ObjectMapper inside. You don't have to inject any beans. just use the Util. Here is Javadoc for the JsonUtils class. It comes with the java Open Source MgntUtils library written and maintained by me. You can get it as Maven artifacts or in Github.