It's easy to create a custom ObjectMapper for Spring, but the configuration requires XML. I'm trying to reduce the amount of XML configuration for things that really aren't going to change without requiring a redeploy of my entire system anyway.
So the title says it all - can I use annotations or some other non-XML method to tell Spring, "Hey, please use my custom object mapper pls"?
EDIT:
This does not appear to work
@Configuration
@EnableWebMvc
public class AppConfig {
@Primary
@Bean
public ObjectMapper mapper(){
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
mapper.registerModule(new JodaModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
EDIT 2: I do not believe that Spring is using my ObjectMapper. I have this code:
@Primary
@Bean
public ObjectMapper mapper(){
ObjectMapper mapper = new ObjectMapper();
JodaModule mod = new JodaModule();
mod.addSerializer(DateTime.class, new JsonSerializer<DateTime>() {
@Override
public void serialize(DateTime dateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
System.out.println("Hi, bob");
}
});
mapper.registerModule(mod);
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
return mapper;
}
but when I set a breakpoint on System.out.println("Hi, bob")
it is never called - Though I'm definitely serializing a DateTime.
When using JSON format, Spring Boot will use an ObjectMapper instance to serialize responses and deserialize requests. In this article, we will take a look at the most common ways to configure the serialization and deserialization options.
Simplest usage is of form: final ObjectMapper mapper = new ObjectMapper(); // can use static singleton, inject: just make sure to reuse! MyValue value = new MyValue(); // ... and configure File newState = new File("my-stuff. json"); mapper.
So, by simply using the RestTemplateBuilder our RestTemplate will automatically use a MappingJackson2HttpMessageConverter configured with an ObjectMapper that uses the required ParameterNamesModule.
Assuming no configuration is required, just a plain ObjectMapper will do. It's OK to be declared as static. But in environment like Spring, IMHO you should declare it as a bean. Spring will manage its lifecycle for you.
You can always follow the steps provided in the Spring Docs.
If you want to replace the default
ObjectMapper
completely, define a@Bean
of that type and mark it as@Primary
.Defining a
@Bean
of typeJackson2ObjectMapperBuilder
will allow you to customize both defaultObjectMapper
andXmlMapper
(used inMappingJackson2HttpMessageConverter
andMappingJackson2XmlHttpMessageConverter
respectively).
So, either you define a @Bean
with your ObjectMapper
like this:
@Primary
@Bean
public ObjectMapper mapper() {
// Customize...
return new ObjectMapper().setLocale(Locale.UK);
}
Or, you define a @Bean
of type Jackson2ObjectMapperBuilder
and customize the builder like this:
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// Customize
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
return builder;
}
This blog entry describes the customization further.
In order to register beans using the @Bean
annotation you must declare the @Bean
in a @Configuration
class as described in the Spring docs about Java-based container configuration.
This seems to be a bug. The Spring Boot documentation says that annotating an ObjectMapper Bean with @Primary should make the Spring context use it instead of the Spring's default mapper. However, this doesn't seem to work. I have found a workaround without using XML.
//Since Spring won't use the custom object mapper Bean defined below for
//HTTP message conversion(eg., when a Java object is returned from a controller,
//and should be converted to json using Jackson), we must override this method
//and tell it to use a custom message converter. We configure that custom converter
//below to use our customized Object mapper.
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
}
//configures the converter to use our custom ObjectMapper
private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
//this line here
jsonConverter.setObjectMapper(objectMapper());
return jsonConverter;
}
//Primary annotation tells the Spring container to use this
//mapper as the primary mapper, instead of
//the Spring's defaultly configured mapper. Primary annotation
// DOESN'T work for some reason(this is most likely a bug and will be resolved in the future.
// When resolved, this Bean will be all it takes to tell Spring to use this ObjectMapper everywhere)
// That means that there won't be a need to configure the Http message converters manually(see method above).
@Primary
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
configureObjectMapper(mapper);
return mapper;
}
//configure ObjectMapper any way you'd like
//This configuration tells the ObjectMapper to
//(de)serialize all fields(private,protected,public,..) of all objects
//and to NOT (de)serialize any properties(getters,setters).
private void configureObjectMapper(ObjectMapper mapper) {
//properties for jackson are fields with getters and setters
//sets all properties to NOT be serialized or deserialized
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
//tell the mapper to traverse all fields and not only default
//default=public fields + fields with getters and setters
//set all fields to be serialized and deserialized
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With