How to set PropertyNamingStrategy for RestTemplate in SpringBoot?

Viewed 15147

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.

4 Answers

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.

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();
    }
}

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;
}
Related