I have a question related to the Jackson configuration on my Spring boot project
As described on spring boot blog
I try to customize my Object serialization.
After added a new config bean in my config
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
return builder;
}
When I try to output an instance of my class User the json result is not in CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
Class User {
private String firstName = "Joe Blow";
public String getFirstName() {
return firstName;
}
}
json output is :
{
"firstName": "Joe Blow"
}
and not
{
"first_name": "Joe Blow"
}
Maybe I need to register something in my Jersey config to activate my custom obejctMapper Config
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("my.package);
}
}
Thanks
The general way to configure the ObjectMapper for JAX-RS/Jersey applications is use a ContextResolver. For example
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
It should be picked up with the package scan, or you can explicitly register it, if it's not within the package scope
public JerseyConfig() {
register(new ObjectMapperContextResolver());
// Or if there's is an injection required
// register it as a .class instead of instance
}
The ContextResolver is called during the marshalling and unmarshalling. The class/type being serialzed or deserialized into will be passed to the getContext method. So you could even use more than one mapper for different types, or even more use cases.
UPDATE
Starting from Spring Boot 1.4, you can just create an ObjectMapper Spring bean, and Spring Boot will create the ContextResolver for you, and use your ObjectMapper
// in your `#Configuration` file.
#Bean
public ObjectMapper mapper() {}
Related
I have two ObjectMappers configured within a springboot application. I declare these in a configuration class like the following:
#Configuration
public class Config {
#Bean
#Primary
public ObjectMapper getPrimary() {
return new ObjectMapper();
}
#Bean
public ObjectMapper getSecondary() {
return new ObjectMapper();
}
}
The #Primary ObjectMapper works without issue. I'm at a loss in understanding how to get a #RestController to use the secondary ObjectMapper though. Any help is appreciated.
you need to use qualifiers:
#Configuration
public class Config {
#Bean("myPrimaryOjectMapper")
#Primary
public ObjectMapper getPrimary() {
return new ObjectMapper();
}
#Bean("mySecondaryOjectMapper")
public ObjectMapper getSecondary() {
return new ObjectMapper();
}
}
and then when injecting:
#Autowired
#Qualifier("mySecondaryOjectMapper")
private ObjectMapper objectMapper;
You can read more for example here: https://www.baeldung.com/spring-qualifier-annotation#qualifierVsPrimary
FWIW, I wasn't able to get it to work with the #RestController implicitly, but I ended up injecting the secondary ObjectMapper into the rest controller, and then directly using the mapper to parse the input.
In Spring boot, is it possible to have many different versions of gson or Jackson http converters and use them dynamically whenever I need a specific type of data format?
You have to create two beans for GsonHttpMessageConverter the first one with settings by default and second one with setting for serializing nulls by following way:
#Bean
public GsonHttpMessageConverter gsonHttpMessageConverter() {
return buildGsonHttpMessageConverter(MapperUtil.getGsonInstance());
}
#Bean
public GsonHttpMessageConverter gsonHttpMessageConverterWithNulls() {
return buildGsonHttpMessageConverter(MapperUtil.getGsonInstanceSerializeNulls());
}
private GsonHttpMessageConverter buildGsonHttpMessageConverter(final Gson gson) {
final GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
converter.setGson(gson);
return converter;
}
And when you want to use one of them then call #Qualifier("someBean") annotation. by following way:
#Autowired
#Qualifier("gsonHttpMessageConverter")
GsonHttpMessageConverter gsonHttpMessageConverter;
#Autowired
#Qualifier("gsonHttpMessageConverterWithNulls")
GsonHttpMessageConverter gsonHttpMessageConverterWithNulls;
I am trying to send an event using RabbitMQ and SpringBoot.
#Configuration class:
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
Event class:
public class TheEvent{
#JsonFormat(pattern = "dd::MM::yyyy")
private LocalDateTime date;
//setters getters
}
When I send it, it arrives as:
{"month":"JULY","year":2018,"dayOfMonth":12,"dayOfWeek":"THURSDAY","dayOfYear":193,"hour":16,"minute":29,"nano":835000000,"second":24,"monthValue":7,"chronology":{"id":"ISO","calendarType":"iso8601"}},"direction":1}"
How can I serialize this date object in predefined pattern? (Remember that I just registering bean Jackson2JsonMessageConverter)
Also tried this:
#Bean
#Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
return objectMapper;
}
It works for me:
#Bean
public Jackson2JsonMessageConverter converter(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
return new Jackson2JsonMessageConverter(objectMapper);
}
In project I used RabbitListener and that Jackson2JsonMessageConverter
Listener:
#RabbitListener(queues = "${spring.rabbitmq.queue}")
#Transactional
public void receiveSocialPost(SocialPost socialPost) {
}
I have the below Json
{
"user": {
"name": "Ram",
"age": 27
}
}
which I want to de-serialize into an instance of the class
public class User {
private String name;
private int age;
// getters & setters
}
For this, I have used #JsonRootName on class name and something like below
#Configuration
public class JacksonConfig {
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(DeserializationFeature.UNWRAP_ROOT_VALUE);
return builder;
}
}
But it did not work as expected. If I send something like below, it worked.
{
"name": "Ram",
"age": 27
}
But I want to get the json de-serialized with root name. Can any one please suggest?
I want to spring boot way of doing this.
#JsonRootName is a good start. Use this annotation on User class and then enable UNWRAP_ROOT_VALUE deserialization feature by adding:
spring.jackson.deserialization.UNWRAP_ROOT_VALUE=true
to your application.properties.
Read more about customizing Jackson mapper in Spring Boot Reference
Using ObjectMapper you can resolve this issue easily. Here's what you have to do :
- Annotate User class as given below
#JsonRootName("user")
public class User {
private String name;
private int age;
// getters & setters
}
Create CustomJsonMapper class
public class CustomJsonMapper extends ObjectMapper {
private DeserializationFeature deserializationFeature;
public void setDeserializationFeature (DeserializationFeature deserializationFeature) {
this.deserializationFeature = deserializationFeature;
enable(this.deserializationFeature);
}
}
Equivalent Spring configuration
<bean id="objectMapper" class=" com.cognizant.tranzform.notification.constant.CustomJsonMapper">
<property name="deserializationFeature" ref="deserializationFeature"/>
</bean>
<bean id="deserializationFeature" class="com.fasterxml.jackson.databind.DeserializationFeature"
factory-method="valueOf">
<constructor-arg>
<value>UNWRAP_ROOT_VALUE</value>
</constructor-arg>
</bean>
Using following code you can test
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
ObjectMapper objectMapper = (ObjectMapper) context
.getBean("objectMapper");
String json = "{\"user\":{ \"name\": \"Ram\",\"age\": 27}}";
User user = objectMapper.readValue(json, User.class);
In Spring boot 1.2.5 with a Jersey 2 interface, how can I set the JSON marshaller to not include fields that have null values?
For example:
[
{
"created": 1433987509174,
"lastModified": 1433876475580,
"id": 1,
"example": "example1b"
},
{
"created": 1434502031212,
"lastModified": 1434502031212,
"id": 10000,
"example": "example1c"
},
{
"created": 1439151444176,
"lastModified": 1439151444176,
"id": 10011,
"example": null
}
]
The field "example": null should not be included in the json output at all, but here it is specifying it is null.
In my #SpringBootApplication class, I've tried adding:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
converter.setObjectMapper(objectMapper);
return converter;
}
or
#Bean
#Primary
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return builder;
}
or
#Primary
#Bean
public ObjectMapper mapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
and/or adding #JsonSerialize(include = Inclusion.NON_NULL) to the Object itself
But it still produces the same response above with the "example": null included.
It was working on Spring 3.0.7 with #JsonSerialize(include=Inclusion.NON_NULL) but that no longer works now that I've ported to Spring Boot 1.2.5.
I believe I've followed the documentation http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper and it's not working so I'm hoping someone might see something I'm missing? Thanks in advance!
Edit: Also just tried adding the class:
#Configuration
public class WebConfiguration extends WebMvcAutoConfiguration {
#Primary
#Bean
public ObjectMapper mapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
}
Solution:
package com.my.spring;
import javax.ws.rs.ext.ContextResolver;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletProperties;
import org.springframework.context.annotation.Configuration;
import com.my.spring.service.rs.MyRestServiceImpl;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
#Configuration
public class JerseyConfiguration extends ResourceConfig {
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
public JerseyConfiguration() {
register(new ObjectMapperContextResolver());
register(MyRestServiceImpl.class); // My jax-rs implementation class
property(ServletProperties.FILTER_FORWARD_ON_404, true); // Not needed for this non_null issue
}
}
I don't know about mixing the Spring way (of configuring the mapper) and how Jersey handles this. But the Jersey way to configure the ObjectMapper is through a ContextResolver, as seen in this answer.
Then register the ObjectMapperContextResolver with your Jersey configuration.
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
...
register(ObjectMapperContextResolver.class);
}
}
Or if you are package scanning, the #Provider annotation will pick up the class.