简体   繁体   English

如何在 SpringBoot 中为 RestTemplate 设置 PropertyNamingStrategy?

[英]How to set PropertyNamingStrategy for RestTemplate in SpringBoot?

I have written a SpringBoot app that consumes a rest api and presents a rest api.我写了一个 SpringBoot 应用程序,它消耗了一个 rest api 并呈现了一个 rest api。 My model pojo's have camelCase named properties.我的模型 pojo 具有驼峰命名的属性。 The json that the app consumes has under_score property names.应用程序使用的 json 具有 under_score 属性名称。 The json that the app produces has under_score property names.应用程序生成的 json 具有 under_score 属性名称。 I would like to use a PropertyNamingStrategy that will do the conversion automatically between Java and json names during marshalling/unmarshalling.我想使用一个 PropertyNamingStrategy,它将在编组/解组期间自动在 Java 和 json 名称之间进行转换。

I have Java config that attempts to set a naming strategy that can handle this;我有 Java 配置,尝试设置可以处理此问题的命名策略;

/**
 * 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.当我的休息服务将 Java pojos 转换为 json 以进行响应时,我可以看到命名策略的 translate 方法被调用。 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;但是,当我通过 RestTemplate 使用 rest api 时,我没有看到它被调用,并且我生成的 pojo 没有从传入的 json 正确初始化; all properties whose name would need translation are null.名称需要翻译的所有属性都为空。 This has forced me to use @JsonProperty on most properties.这迫使我在大多数属性上使用 @JsonProperty。 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.我有很多属性和很多 pojos - 这是 SpringBoot 应该提供帮助的一种非常不优雅的样板解决方案。 Is there a way I can set a PropertyNamingStrategy that RestTemplate will use to convert the incoming json names from under_score to camelCase?有没有办法设置一个 PropertyNamingStrategy,RestTemplate 将使用它来将传入的 json 名称从 under_score 转换为 camelCase?

Thanks for your help.感谢您的帮助。

When creating a RestTemplate you need to set the objectMapper to yours.创建RestTemplate您需要将objectMapper设置为您的。 Also, you should declare your custom ObjectMapper as a @Bean so it is constructed by Spring as a singleton and managed for you.此外,您应该将自定义 ObjectMapper 声明为@Bean以便 Spring 将其构造为单例并为您管理。 Do the same for the PropertyNamingStrategy , instead of 'newing' it up and declaring the class as static.PropertyNamingStrategy做同样的事情,而不是“更新”它并将类声明为静态。

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

Just add this annotation above POJO's which u will be sending or receiving in ur request response.只需在您将在请求响应中发送或接收的 POJO 上方添加此注释。

@JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class) @JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)

PS the strategy can be different depending upon the requirement. PS,策略可以根据要求而有所不同。

A shorter answer is to use the Spring's objectMapper.一个简短的答案是使用 Spring 的 objectMapper。 The benefit is that it shares the same configuration in application.properties .好处是它在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.因此,您可以设置spring.jackson.property-naming-strategy=SNAKE_CASE或其他任何内容,并且在包括 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM