How to set PropertyNamingStrategy for RestTemplate in SpringBoot?
Solution 1
When creating a RestTemplate
you need to set the objectMapper
to yours.
Also, you should declare your custom ObjectMapper as a @Bean
so it is constructed by Spring as a singleton and managed for you. Do the same for the PropertyNamingStrategy
, instead of 'newing' it up and declaring the class as static.
public class RestConfig
{
/**
* Bean to make jackson automatically convert from
* camelCase (java) to under_scores (json) in property names
*
* @return ObjectMapper that maps from Java camelCase to json under_score names
*/
@Bean
public ObjectMapper jacksonObjectMapper()
{
return new ObjectMapper().setPropertyNamingStrategy(propertyNamingStrategy());
}
@Bean
public PropertyNamingStrategy propertyNamingStrategy()
{
return new UpperCaseUnderscoreStrategy();
}
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(jacksonObjectMapper());
messageConverters.add(jsonMessageConverter);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
}
And your class is in a separate file? It doesn't need to be static.
/**
* Property naming strategy that converts both ways between camelCase and under_score
* property names.
*/
public static class UpperCaseUnderscoreStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase
{
/**
* Converts camelCase to under_score and
* visa versa. The idea is that this
* name strategy can be used for both
* marshalling and unmarshaling.
*
* For example, "userName" would be converted to
* "user_name" and conversely "user_name" would
* be converted to "userName".
*
* @param input formatted as camelCase or under_score string
* @return input converted to opposite format
*/
@Override
public String translate(String input)
{
if (input == null || input.length() == 0)
{
return input; // garbage in, garbage out
}
//
// we always take the first character;
// this preserves initial underscore
//
StringBuilder sb = new StringBuilder();
final int length = input.length();
int i = 0;
//
// skip initial underscores
//
while ((i < length) && ('_' == input.charAt(i)))
{
sb.append(input.charAt(i));
i += 1;
}
while (i < length)
{
//
// find underscores, remove and capitalize next letter
//
while ((i < length) && ('_' != input.charAt(i)) && !Character.isUpperCase(input.charAt(i)))
{
sb.append(input.charAt(i));
i += 1;
}
if(i < length)
{
if('_' == input.charAt(i))
{
// underscore to uppercase
//
// skip underscores
//
while ((i < length) && ('_' == input.charAt(i)))
{
// skip underscores
i += 1;
}
//
// capitalize
//
if (i < length)
{
sb.append(Character.toUpperCase(input.charAt(i)));
i += 1;
}
}
else // uppercase to unscore + lowercase
{
sb.append('_');
sb.append(Character.toLowerCase(input.charAt(i)));
i += 1;
}
}
}
return sb.toString();
}
}
Solution 2
Just add this annotation above POJO's which u will be sending or receiving in ur request response.
@JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
P.S. the strategy can be different depending upon the requirement.
Solution 3
A shorter answer is to use the Spring's objectMapper. The benefit is that it shares the same configuration in application.properties
. So you can set spring.jackson.property-naming-strategy=SNAKE_CASE
or whatever there, and it's consistent across the entire application including RestTemplate. Code as follows.
@Configuration
@RequiredArgsConstructor
public class HTTPConfig {
public final ObjectMapper objectMapper; // provided by spring
@Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder()
.messageConverters(new MappingJackson2HttpMessageConverter(objectMapper))
.build();
}
}
Solution 4
I suggest you use this method. You can also add it as a spring bean.
private RestTemplate getRestTemplate() {
final RestTemplate restTemplate = new RestTemplate();
final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
final MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE));
messageConverters.add(jsonMessageConverter);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
Comments
-
Ezward almost 2 years
I have written a SpringBoot app that consumes a rest api and presents a rest api. My model pojo's have camelCase named properties. The json that the app consumes has under_score property names. The json that the app produces has under_score property names. I would like to use a PropertyNamingStrategy that will do the conversion automatically between Java and json names during marshalling/unmarshalling.
I have Java config that attempts to set a naming strategy that can handle this;
/** * Configuration for Rest api. * <p> * Created by emurphy on 2/25/16. */ @Configuration public class RestConfig { /** * Bean to make jackson automatically convert from * camelCase (java) to under_scores (json) in property names * * @return ObjectMapper that maps from Java camelCase to json under_score names */ @Bean public ObjectMapper jacksonObjectMapper() { return new ObjectMapper().setPropertyNamingStrategy(new UpperCaseUnderscoreStrategy()); } /** * Property naming strategy that converts both ways between camelCase and under_score * property names. */ public static class UpperCaseUnderscoreStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase { /** * Converts camelCase to under_score and * visa versa. The idea is that this * name strategy can be used for both * marshalling and unmarshaling. * * For example, "userName" would be converted to * "user_name" and conversely "user_name" would * be converted to "userName". * * @param input formatted as camelCase or under_score string * @return input converted to opposite format */ @Override public String translate(String input) { if (input == null || input.length() == 0) { return input; // garbage in, garbage out } // // we always take the first character; // this preserves initial underscore // StringBuilder sb = new StringBuilder(); final int length = input.length(); int i = 0; // // skip initial underscores // while ((i < length) && ('_' == input.charAt(i))) { sb.append(input.charAt(i)); i += 1; } while (i < length) { // // find underscores, remove and capitalize next letter // while ((i < length) && ('_' != input.charAt(i)) && !Character.isUpperCase(input.charAt(i))) { sb.append(input.charAt(i)); i += 1; } if(i < length) { if('_' == input.charAt(i)) { // underscore to uppercase // // skip underscores // while ((i < length) && ('_' == input.charAt(i))) { // skip underscores i += 1; } // // capitalize // if (i < length) { sb.append(Character.toUpperCase(input.charAt(i))); i += 1; } } else // uppercase to unscore + lowercase { sb.append('_'); sb.append(Character.toLowerCase(input.charAt(i))); i += 1; } } } return sb.toString(); } }
I can see the naming strategy's translate method getting called when my rest service converts Java pojos to json for the response. However, when I'm consuming a rest api, via RestTemplate, I don't see this get called and my resulting pojos are not correctly intialized from the incoming json; all properties whose name would need translation are null. This has forced me to use @JsonProperty on most properties. I have a lot of properties and a lot of pojos - this is a very inelegant, boilerplate kind of solution that SpringBoot is supposed to help with. Is there a way I can set a PropertyNamingStrategy that RestTemplate will use to convert the incoming json names from under_score to camelCase?
Thanks for your help.
-
Ezward about 8 yearsThanks for the reply. I've implemented this but it still does not work. When I remove the @JsonProperty annotations in my pojos, I can still see that only properties with simple names, like 'description', get filled in when I make a rest call. If I set a break point in the instance of UpperCaseUnderscoreStrategy.translate(), it never stops there so it is clear that the rest template is not calling it.
-
Ezward about 8 yearsI can see when debugging that a MappingJackson2HttpMessageConverter is chosen from the list of messageConverters. I've traced further into the ObjectMapper and I can see that the DeserializationConfig returned by ObjectMapper.getDeserializationConfig() has it's _propertyNameStrategy value of null, so whatever we've done so far has not gotten into the deserialization pipeline.
-
Ezward about 8 yearsOk, I have figure this out. Your answer was very helpful. I found out that my low level code was new'ing up a RestTemplate on each call, so the configured template was ignored. Once I changed to autowire the rest template into my low level, everything worked. Now I see the translate() method being called as expected. Thanks for your help.
-
francisco neto over 2 yearsThanks! using the objectMapper
new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
did the trick for me.