Is there a way to create two instances of ObjectMapper for different purpose.
Modified ObjectMapper
#Component
class MyObjectMapper extends ObjectMapper{
public MyObjectMapper(){
super();
}
public MyObjectMapper(MyObjectMapper om) {
super(om);
}
#Override
public MyObjectMapper copy() {
return new MyObjectMapper(this);
}
}
Now use it as follows
#Autowired ObjectMapper objectMapper; //performs standard serialization
#Autowire MyObjectMapper myMapper; //i can add custom filters in thiis mapper.
I tried a similar setup but the custom mapper actually affects the original mapper all the rest controllers throw JSON parse error: Unrecognized field
Edit: Not sure if this point is very important but still adding it
Am using spring-boot-starter-json
And that's exactly where you should use #Qualifier annotation.
This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring. It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.
OK. Combining with Answer from Aniket figured out what is wrong and still looking for some more explanation.
Instead of instantiating the ObjectMapper as new ObjectMapper(). Building it with Mapper fixed it.
So, two have multiple instance of ObjectMapper
#Primary
#Bean
public ObjectMapper objectMapper(){
return new Jackson2ObjectMapperBuilder()
.build();
}
#Bean("customMapper")
public ObjectMapper customMapper(){
ObjectMapper customMapper = new Jackson2ObjectMapperBuilder().build();
mapper.<your customization , filters, providers etc;>
return mapper;
}
The #Primary will be used by in all default cases i.e When you simply #Autowire or the default serialization applied to your Response/Request Body by your controller.
To use your Custom Mapper, explicitly use with the Bean ID.
#Autowired #Qualifier("customMapper") ObjectMapper mapper;
Related
I'm trying to implement RFC 7807 in my Spring Boot project using zalando problem-spring-web https://github.com/zalando/problem-spring-web
I've done the setup according to this guide https://github.com/zalando/problem-spring-web/tree/master/problem-spring-web
When an exception is thrown, the Problem instance does get generated, but its serialized JSON form is not as expected, most notably the stack trace is included when it shouldn't.
After some debugging, it seems that the ProblemModule is not registered in the ObjectMapper that is used to serialize the problem (its setupModule method is never called). I was under the impression that declaring a bean of type Module was enough to have it picked up by Spring and registered in the ObjectMapper, but it doesn't happen here.
The doc says
In case you want to enable stack traces, please configure your
ProblemModule as follows:
ObjectMapper mapper = new ObjectMapper()
.registerModule(new ProblemModule().withStackTraces());
which seems to imply that you need to instantiate your own ObjectMapper, but then how to make sure that it's used by the library when deserializing the Problem?
Since I can't get the ObjectMapper to register my Modules I figured I had to do it myself so I came up with this solution that seems to work:
#Configuration
public class ProblemConfiguration implements InitializingBean {
#Autowired
ObjectMapper objectMapper;
#Override
public void afterPropertiesSet() {
objectMapper.registerModules(
new ProblemModule(),
new ConstraintViolationProblemModule()
);
}
}
If someone has an idea why it's not working as expected, I'd be glad to hear it :)
Since Spring boot 1.1.0, the JacksonAutoConfiguration creates an ObjectMapper bean and automatically registers all module found in your registered beans,
so you juste need to create does two beans modules and use de already configured ObjectMapper like this:
/*
* Module for serialization/deserialization of RFC7807 Problem.
*/
#Bean
public ProblemModule problemModule() {
return new ProblemModule();
}
/*
* Module for serialization/deserialization of ConstraintViolationProblem.
*/
#Bean
public ConstraintViolationProblemModule constraintViolationProblemModule() {
return new ConstraintViolationProblemModule();
}
use configured ObjectMapper for ex in service Classe
#Autowired
ObjectMapper jacksonObjectMapper
Just for clarification, I have a #Component class with two wired beans. I want to prioritize each bean. Is that possible to do?
#Component
public class SecureMessages {
#Autowired // load second
private ObjectMapper objectMapper;
#Autowired // load first
private LocalCacheServiceImpl LocalCacheServiceImpl;
}
As per the above code I want to create a bean for local cache initially while calling this component class and then the rest of the beans can get created.
You can add #Order annotation to your components. Lower order value has a higher priority. You can read more about it here.
#Bean
#Order(2)
public ObjectMapper getObjectMapper(){
return new ObjectMapper();
}
#Bean
#Order(1)
public LocalCacheServiceImpl getLocalCacheServiceImpl(){
return new LocalCacheServiceImpl();
}
#DependsOn could maybe also be suitable for you. It depends why you want order your beans. You can read about it here
I have a multi-module Maven-based Spring Boot Application. In one of my modules' application.properties file, I am setting Jackson to not serialize dates as timestamps (arrays), so that they are always serialized as strings (which is the fallback behavior). The property is this:
spring.jackson.serialization.write_dates_as_timestamps=false
Problem is... the property doesn't seem to be picked up by either Spring Boot/Jackson. The java.time.LocalDate instances I am trying to serialize always get written as timestamps. I have checked the code inside the LocalDateSerializer.serialize(...) method (from Jackson's own JavaTimeModule), and found this:
#Override
public void serialize(LocalDate date, JsonGenerator generator, SerializerProvider provider) throws IOException
{
if (useTimestamp(provider)) { // This always returns true
generator.writeStartArray();
generator.writeNumber(date.getYear());
generator.writeNumber(date.getMonthValue());
generator.writeNumber(date.getDayOfMonth());
generator.writeEndArray();
} else {
String str = (_formatter == null) ? date.toString() : date.format(_formatter);
generator.writeString(str);
}
}
The useTimestamp() method always returns true, no matter what my configuration is. :(
Maybe you can create a custom MappingJackson2HttpMessageConverter and an ObjectMapper. Here is an example configuration class,
#Configuration
public class MyConfiguration extends WebMvcConfigurerAdapter {
#Bean
public MappingJackson2HttpMessageConverter
getMappingJacksonHttpMessageConverter() {
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter();
...
ObjectMapper mapper = new ObjectMapper();
...
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
//or
// mapper.configure(
// SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
converter.setObjectMapper(mapper);
return converter;
}
}
Thanks to Indra's answer, I got an important clue that helped me figure out the issue.
Turns out that my Spring Boot application has multiple instances of RestTemplate in its application context (it is a pretty big system and relies upon some custom proprietary frameworks to work). Most of these RestTemplate instances were there just to support my system's architecture. There is only one RestTemplate instance that I should've cared about: The one that I wrote and allows my business logic to work (by enabling my system to consume remote RESTful APIs).
So, when I was trying to configure Jackson and its ObjectMapper, I wasn't really affecting my RestTemplate instance. By the time my configuration was read, that RestTemplate had long ago been initialized with the default settings provided by Spring Boot.
This is how my final configuration class looks:
#Configuration
public class RestConfiguration {
#Bean
public RestTemplate myRestTemplate(HttpMessageConverters customConverters) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(customConverters.getConverters());
return restTemplate;
}
/*
* The following custom HttpMessageConverter is injected into myRestTemplate
* in order to appropriately configure the serialization/deserialization of LocalDate
* instances into/from strings.
*/
#Bean
public HttpMessageConverters customConverters() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
converter.setObjectMapper(mapper);
return new HttpMessageConverters(converter);
}
}
What I am doing is to explicitly configure the RestTemplate instance that needed to be configured.
The following documentation was also very helpful: HttpMessageConverters. It describes how to declare custom HttpMessageConverters for use in the application context.
I have a bean:
#Bean
public ObjectMapper jsonMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
return objectMapper;
}
The problem is that while it's being injected into my classes, at the same time it's injected into one of Spring classes since autowiring by type is performed. I don't want WRAP_ROOT_VALUE to be set in the bean that Spring class is using. Is there any options to keep Spring from autowiring it into its classes besides not using beans in this case at all?
Use the name attribute in your #Bean annotation. Then it'll only be injected into classes that make reference to this name.
I'm using spring-boot and want to customize the ObjectMapper created.
What I want to do is be able to serialize objects that do not have a getter or setters. Before this could be done by putting JsonAutoDetect.Visibility.ANY on the ObjectMapper.
But how can I enable this feature using the Jackson2ObjectMapperBuilder bean I'm currently exposing ?
You can use a Jackson2ObjectMapperBuilder subclass that overrides the configure(ObjectMapper) method:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
return new Jackson2ObjectMapperBuilder() {
#Override
public void configure(ObjectMapper objectMapper) {
super.configure(objectMapper);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
}
};
}
If you want to keep the ObjectMapper configurable through the spring.jackson.* properties that Spring Boot provides, then you better don't define your own Jackson2ObjectMapperBuilder bean (check JacksonObjectMapperBuilderConfiguration inside JacksonAutoConfiguration class for details).
What you can do instead is this:
#Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder mapperBuilder) {
return mapperBuilder.build().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
}
I spend a half of day to play with different settings.
So I manage to work it (1.3.2.RELEASE) when:
I configure jackson in simple #Configuration annotated config class (not extended from WebMvcConfigurerAdapter)
I have NOT#EnableWebMvc
Then Jackson2ObjectMapperBuilder objectMapperBuilder solution is
work, but spring.jackson.serialization.indent_output: true in properties ignored.
At last I finished with
#Autowired(required = true)
public void configeJackson(ObjectMapper objectMapper) {
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}
But all this is puzzle for me. I wrote a question about any explanation of all this magic in order to have some undestanding and solve problem not by
trial-and-error method: Are there any Spring Boot documentation for understanding how web mvc configuration is work?