MappingJacksonHttpMessageConverter cannot be resolved to a type - spring

I am trying to upgrade Spring Application (not using Spring Boot) from 2.3.2.RELEASE to 5.3.20 (latest version as of today), I do see below code is breaking and
Code:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
ObjectMapper defaultMapper = new ObjectMapper();
defaultMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, true);
defaultMapper.configure(SerializationConfig.Feature.REQUIRE_SETTERS_FOR_GETTERS, true);
mappingJacksonHttpMessageConverter.setObjectMapper(defaultMapper);
converters.add(mappingJacksonHttpMessageConverter);
}
What's the replacement of import org.codehaus.jackson.map.ObjectMapper to jacksondata bind for below code?
ObjectMapper defaultMapper = new ObjectMapper();
defaultMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, true);
defaultMapper.configure(SerializationConfig.Feature.REQUIRE_SETTERS_FOR_GETTERS, true);
mappingJacksonHttpMessageConverter.setObjectMapper(defaultMapper);

I was able to do like below
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
objectMapper.configure(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS, true);
mappingJacksonHttpMessageConverter.setObjectMapper(objectMapper);

The MappingJacksonHttpMessageConverter is for Jackson1, which isn't supported anymore with Spring 5, it was deprecated in Spring 4.
You will need to migrate to Jackson2 and use the MappingJackson2HttpMessageConverter instead.
Instead of constructing the ObjectMapper manually I strongly suggest using the Jackson2ObjectMapperBuilder to construct the ObjectMapper. It will automatically detect and register well known Modules and allows for easier configuration.
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper mapper = Jackson2ObjectMapperBuilder.json()
.defaultViewInclusion(true)
.featuresToEnable(SerializationConfig.Feature.REQUIRE_SETTERS_FOR_GETTERS)
.build();
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter(mapper);
converters.add(converter);
}
Finally use the constructor instead of the setter as that saves building an additional ObjectMapper (done in the default constructor of the MappingJackson2HttpMessageConverter and then throwing it away) saves a few CPU cycles.

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.

Spring Jackson custom Deserializer does not called

I have a Spring project, and I try to add a custom deserializer to deserialize Date properties depend on their format.
If I use it as annotation on Date property, it works fine.
But if I add the deserializer to my object mapper, it does not called when Jackson deserialize a date.
I try to apply my custom deserializer like this:
#Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Date.class, new DateDeserializer());
mapper.registerModule(module);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
return mapper;
}
I don't want to apply an annotation on Date properties every time, I want to use this deserializer by default.
What I do wrong?
Thanks the help for everyone.
Finally I found the answer at spring.io.
#Configuration
#EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.deserializerByType(Date.class, new DateDeserializer());
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}
Given that if you use the deserializer in an annotation on Date property then I would say that this ObjectMapper is not being used for deserialization. Try the following:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
I have doubt how you try to use this ObjectMapper bean inside your application.
I trust you already know that this bean is need to created inside a Configuration class. If not your bean will not register in the context. Like this for example,
#Configuration
public class MapperConfig {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Date.class, new DateDeserializer());
mapper.registerModule(module);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
return mapper;
}
}
The second problem might be how you try use this ObjectMapper bean. If your create new instance of ObjectMapper like this ObjectMapper objectMapper = new ObjectMapper();, that instance will not have your custom deserializer and stuff. What you can do is #Autowire the ObjectMapper instance that you have already created.

Corda RPC JacksonSupport.createDefaultMapper to use ObjectMapper in Spring client

How to register newly introduced Corda RPC ObjectMapper in Spring Boot?
Even after having below code in #Configuration class Jackson failing to serialize Party object to JSON string.
#Bean
public JsonComponentModule jsonComponentModule() {
return new JsonComponentModule();
}
#Bean
#Primary
public ObjectMapper cordaRpcObjectMapper(NodeRPCConnection rpc) {
ObjectMapper objectMapper = JacksonSupport.createDefaultMapper(rpc.getProxy(), new JsonFactory(), true);
objectMapper.registerModule(jsonComponentModule());
return objectMapper;
}
After some tweaks I'm successfully able to register Corda RPC ObjectMapper with Jackson with below code.
//Register any other custom (de)Serializer classes.
#Bean
public Module jsonComponentModule() {
return new JsonComponentModule();
}
//Force Spring/Jackson to use only provided Corda ObjectMapper for serialization.
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(#Autowired NodeRPCConnection rpcConnection) {
ObjectMapper mapper = JacksonSupport.createDefaultMapper(rpcConnection.getProxy()/*, new JsonFactory(), true*/);
mapper.registerModule(jsonComponentModule());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(mapper);
return converter;
}

Spring Cloud Stream #StreamListener Custom MappingJackson2MesageConverter

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.

Configure a Jackson's DeserializationProblemHandler in Spring environment [duplicate]

This question already has answers here:
Can't set ProblemHandler to ObjectMapper in Spring Boot
(2 answers)
Closed 4 years ago.
As I understood, Spring is already providing a bean for Jackson ObjectMapper. Therefore, instead of creating a new bean, I'm trying to customize this bean.
From this blog post, and then this Github project I used Jackson2ObjectMapperBuilder bean to achieve this customization.
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(ApplicationContext context) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.findModulesViaServiceLoader(true);
return builder;
}
Then, I was trying to customize the deserializer in order to make it lenient: if an exception is raised when deserializing a property, I want the result object's property to be null and let the deserialization continue (default is to fail on first property that cannot be deserialized).
I've been able to achieve that with a class NullableFieldsDeserializationProblemHandler that extends DeserializationProblemHandler (I do not think the code is relevant but if needed, I can share it).
The simplest way to register this handler is to use the .addHandler() method of ObjectMapper. But of course, doing like this, I would need to set that every time I inject and use the ObjectMapper. I'd like to be able to configure handler so that every time the ObjectMapper is auto-wired, the handler is already present.
The best solution I came up with so far is to use a #PostConstruct annotation only to register the problem handler.
#Configuration
public class JacksonConfiguration implements InitializingBean {
#Autowired private ObjectMapper objectMapper;
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(ApplicationContext context) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.findModulesViaServiceLoader(true);
return builder;
}
#Override
public void afterPropertiesSet() {
objectMapper.addHandler(new NullableFieldsDeserializationProblemHandler());
}
}
But the problem of this solution is that it seems I can still access an autowired ObjectMapper that doesn't have yet registered the problem handler (I can see it happening after when I need it in debug mode).
Any idea how I should register this handler? I've noticed Jackson2ObjectMapperBuilder has a .handlerInstantiator() but I couldn't figure out how to use it.
Note I've also tried with Jackson2ObjectMapperBuilderCustomizer since I'm using Spring Boot but had no better results.
It's not possible to directly add a DeserializationProblemHandler to the ObjectMapper via a Jackson2ObjectMapperBuilder or Jackson2ObjectMapperBuilderCustomizer. The handlerInstanciator() method is for something else.
However, it's possible to do so by registering a Jackson module:
the builder has a modules() method
the module has access via setupModule() to a SetupContext instance, which has a addDeserializationProblemHandler() method
This works:
#Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return new Jackson2ObjectMapperBuilderCustomizer() {
#Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.modules(new MyModule());
}
};
}
private static class MyModule extends SimpleModule {
#Override
public void setupModule(SetupContext context) {
// Required, as documented in the Javadoc of SimpleModule
super.setupModule(context);
context.addDeserializationProblemHandler(new NullableFieldsDeserializationProblemHandler());
}
}
What about writing a bean like this:
#Configuration
public class ObjectMapperConfiguration {
#Bean
ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// jackson 1.9 and before
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// or jackson 2.0
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
}
This is for global configuration. If, instead, what you want to do is to configure the feature for specific a class, use this annotation above the class definition:
#JsonIgnoreProperties(ignoreUnknown = true)

Resources