Spring Cloud Stream #StreamListener Custom MappingJackson2MesageConverter - spring

I have a custom ObjectMapper configured within my application which has custom modules for deserializing immutable types provided by Guava in Jackson.
My problem is that I cannot seem to override the objectMapper use by #StreamListener such that it deserializes my objects correctly.
Here is what I have tried:
#Bean
public MessageConverter messageConverter() {
final MappingJackson2MessageConverter mappingJackson2MessageConverter = new MappingJackson2MessageConverter();
mappingJackson2MessageConverter.setObjectMapper(objectMapper);
return mappingJackson2MessageConverter;
}
The above snippet is in a class annotated with #Configuration and the objectMapper is defined in the class:
public static final ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))
.registerModule(new GuavaModule());
Any ideas on what I may be missing?
Spring Boot: 1.5.10.RELEASE
Spring Rabbit: 1.7.6.RELEASE
Spring Cloud Stream: 1.2.2.RELEASE
Spring Messaging: 4.3.14.RELEASE

So, it turns out there was no issue with my configuration. My custom ObjectMapper was being selected by the MappingJackson2MessageConverter. However, the message converter was not being selected, because the messages were being sent with the wrong content-type header.
Moral of the story, be careful with the Content-Type and make sure it matches the converter.

Related

Customise JSON date formatting of JSON for spring-mvc (non-boot)?

I am converting my app to get rid of spring-boot, it now uses only Spring (5.3).
I've added the #EnableWebMvc configuration and I have my endpoints working properly for the most part - they return the data I want as JSON.
Previously, I customised the date format with the spring-boot property: spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
In the new pure-spring app though, it's regressed back serializing to a long value.
I've tried the following, but it doesn't seem to even use these beans at all:
#Bean
public ObjectMapper objectMapper() {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
ObjectMapper dateFormatMapper = new ObjectMapper();
dateFormatMapper.setDateFormat(dateFormat);
return dateFormatMapper;
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2JsonView(){
var converter = new MappingJackson2HttpMessageConverter();
converter.getObjectMapper().setDateFormat(
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") );
return converter;
}
I'm looking to customise the format globally, not on a per-field basis.
What would be the equivalent of spring.jackson.date-format for pure Spring #EnableWebMvc setup?
You can customize MappingJackson2HttpMessageConverter by using WebMvcConfigurer with #EnableWebMvc.
For example:
#Configuration
#EnableWebMvc
public class YourConfiguration implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}
For more information, please see 1.11.7. Message Converters - Web on Servlet Stack - docs.spring.io.

How to configure HttpMessageConverter for specified controller in SpringMvc

As we know, we can configure the global HttpMessageConverter by configureMessageConverters method in WebMvcConfigurer.
see https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-config-message-converters
But I want to configure a HttpMessageConverter for specified Controller to override the global configuration to implement the function different from the global.
How should I configure it? Can any friends give me pointers?
You can put below code in any of your configuration classes. and you have to autowire this specific objectmapper in that class where you need.Even you can create multiple objectmappers to serve different purposes.
#Bean
#Qualifier("customForController")
public ObjectMapper getObjectMapper() {
ObjectMapper mapper=new ObjectMapper();
return mapper;
}
#Bean
#Qualifier("customMessageConverter")
public MappingJackson2HttpMessageConverter converter() {
MappingJackson2HttpMessageConverter httConverter = new MappingJackson2HttpMessageConverter();
httConverter.setObjectMapper(getObjectMapper());
//others configuration goes here
return httConverter;
}

Spring return image from controller while using Jackson Hibernate5Module

I am using Spring 4.3.1 and Hibernate 5.1.0 for my webapp.
For Jackson to be able serializing lazy objects I have to add the Hibernate5Module to my default ObjectMapper. This I have done via
#EnableWebMvc
#Configuration
#ComponentScan({ "xxx.controller" })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Autowired
SessionFactory sf;
...
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Hibernate5Module module = new Hibernate5Module(sf);
module.disable(Feature.USE_TRANSIENT_ANNOTATION);
module.enable(Feature.FORCE_LAZY_LOADING);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.modulesToInstall(module);
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
super.configureMessageConverters(converters);
}
}
This is working but if it is enabled serializing a byte[] does not work anymore and fails with HTTP Status 500 - Could not write content: No serializer found for class java.io.BufferedInputStream
So my question is how to extend the default ObjectMapper while preserving the default ones?
I have seen somthing preserving the defaults using Spring Boot but I do not use Spring Boot. Any ideas?
As specified in the WebMvcConfigurer.configureMessageConverters javadoc, "If no converters are added, a default list of converters is registered", i.e. you will have to manually add all the default converters if you are using WebMvcConfigurer. Calling 'super.configureMessageConverters(converters)' does nothing if you extend WebMvcConfigurer. Take a look in 'WebMvcConfigurationSupport.addDefaultHttpMessageConverters(...)' to see all the default message converters, you can also extend this class instead of WebMvcConfigurer, with which you get slightly more clarity what happens.

How to customize the Jackson serializer for Spring SQS

How do you customize the Jackson JSON serializer for SQS? I've googled around, but so far everything I've found is related to the Spring web stuff, and there doesn't seem to be any way to get a hold of a reference to the serializer that Spring SQS uses, so that I can add my custom types (Java 8 Date stuff)
I just had a look at the source code of spring-cloud-aws to see how the Jackson object mapper is being instantiated, see here: QueueMessagingTemplate.java.
It turns out that it has a constructor that takes in a MessageConverter, so you could do this:
#Configuration
public class SpringAwsMessagingConfig {
#Bean
public QueueMessagingTemplate myMessagingTemplate(AmazonSQS amazonSqs, ResourceIdResolver resolver) {
ObjectMapper mapper = new ObjectMapper();
// configure the Jackson mapper as needed
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setSerializedPayloadClass(String.class);
converter.setObjectMapper(mapper);
return new QueueMessagingTemplate(amazonSqs, resolver, converter);
}
}

Spring 4.1's JsonView and Hibernate4Module

I have a project which liberally uses lazy loading via the org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter view filter.
If I disable this, I have to add a new message converter in my rest servlet configuration:
public MappingJackson2HttpMessageConverter jacksonMessageConverter() {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
// Registering Hibernate4Module to support lazy objects
mapper.registerModule(new Hibernate4Module());
messageConverter.setObjectMapper(mapper);
return messageConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// Add the custom-configured HttpMessageConverter
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
However, doing so seems to break the behavior of #JsonView: the existence of the #JsonView annotation no longer seems to filter in/out any properties.
My guess is Hibernate4Module doesn't support #JsonView, but if that's the case, how can I use Spring 4.1's #JsonView support and make Jackson cognizant of lazy-loaded Hibernate entities? The only solution so far seems to be avoiding Hibernate4Module and relying on the OpenEntityManagerInViewFilter filter.
Thanks for any insight you can offer.
Whenever you are using spring 4.1.*, make sure you are changing
import org.codehaus.jackson.annotate.*;
to
import org.fasterxml.jackson.annotate.*;
in your case its
import org.fasterxml.jackson.annotate.JsonView;
I think you need to configure the DEFAULT_VIEW_INCLUSION feature to be false. By default all properties without explicit view definition are included in serialization. Out of box Spring will disable this default for you. So if you want to use your own custom mapper you will probably want to disable it or all the properties with out #JsonView annotations will get added to you JSON.
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);

Resources