使用RestTemplate时如何配置内部Jackson映射器?


77

我想更新Spring RestTemplate所使用的jackson映射器的SerializationConfig.Feature ...属性,任何想法都知道如何获得它或在哪里可以/应该对其进行配置。

Answers:


97

默认RestTemplate构造函数注册一组HttpMessageConverter

this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter());
this.messageConverters.add(new XmlAwareFormHttpMessageConverter());
if (jaxb2Present) {
    this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jacksonPresent) {
    this.messageConverters.add(new MappingJacksonHttpMessageConverter());
}
if (romePresent) {
    this.messageConverters.add(new AtomFeedHttpMessageConverter());
    this.messageConverters.add(new RssChannelHttpMessageConverter());
}

MappingJacksonHttpMessageConverter轮流,造成ObjectMapper直接实例。您可以找到该转换器并替换ObjectMapper或注册一个新的转换器。这应该工作:

@Bean
public RestOperations restOperations() {
    RestTemplate rest = new RestTemplate();
    //this is crucial!
    rest.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
    return rest;
}

@Bean
public MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter() {
    MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
    converter.setObjectMapper(myObjectMapper());
    return converter;
}

@Bean
public ObjectMapper myObjectMapper() {
    //your custom ObjectMapper here
}

在XML中,遵循以下原则:

<bean id="restOperations" class="org.springframework.web.client.RestTemplate">
    <property name="messageConverters">
        <util:list>
            <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
            <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
            <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
            <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"/>
            <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="customObjectMapper"/>
            </bean>
        </util:list>
    </property>
</bean>

<bean id="customObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>

请注意,转换并不是真正的1:1-我必须以messageConvertersXML显式创建列表,而采用@Configuration这种方法,我可以引用现有列表并简单地对其进行修改。但这应该可行。


如果能将其转换为XML,我将不胜感激。真的想尝试此方法,但不确定如何配置。非常感谢!
贾斯汀

9
不知道这是否仅是由于版本差异引起的,但我相信Jackson转换器类的正确名称是MappingJackson * 2 * HttpMessageConverter
nbrooks

1
我可能会避免在现有默认值的开头添加新的mappingJacksonHttpMessageConverter,而是替换现有的Jackson消息转换器。否则,您最终将在列表中有2个Jackson消息转换器。
mvogiatzis

我尝试将其添加到索引0,但遇到了一些非常奇怪的JSON解析异常。删除现有的工作效果更好,如@mvogiatzis建议
Mads Hoel

我需要改变RestOperations -> RestTemplate@Bean定义,使这个作品
独立

29

如果您不使用Spring IOC,则可以执行以下操作(Java 8):

ObjectMapper objectMapper = new ObjectMapper();
// configure your ObjectMapper here

RestTemplate restTemplate = new RestTemplate();    

MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setPrettyPrint(false);
messageConverter.setObjectMapper(objectMapper);
restTemplate.getMessageConverters().removeIf(m -> m.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));
restTemplate.getMessageConverters().add(messageConverter);

1
当您发现需要迭代时,为什么需要删除并重新添加?
lqbweb

1
restTemplate.getMessageConverters()。removeIf(m-> m.getClass()。isAssignableFrom(MappingJackson2HttpMessageConverter.class));
鲁迪格·舒尔茨

14

RestTemplate将初始化其默认消息转换器。您应该用MappingJackson2HttpMessageConverter自己的bean替换,后者应使用要使用的对象映射器。这为我工作:

@Bean
public RestTemplate restTemplate() {
    final RestTemplate restTemplate = new RestTemplate();

    //find and replace Jackson message converter with our own
    for (int i = 0; i < restTemplate.getMessageConverters().size(); i++) {
        final HttpMessageConverter<?> httpMessageConverter = restTemplate.getMessageConverters().get(i);
        if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter){
            restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter());
        }
    }

    return restTemplate;
}

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setObjectMapper(myObjectMapper());
    return converter;
}

@Bean
public ObjectMapper myObjectMapper() {
    // return your own object mapper
}

命名该方法似乎很重要,myObjectMapper()但不仅要如此objectMapper()。由于某种原因,似乎从未调用过该方法,而您获得了默认值ObjectMapper
herman

1
这行得通,但答案似乎有错字?restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter);应该是restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter());(请注意mappingJackson2HttpMessageConverter()中的括号,使其成为方法调用而不是引用)
Jens Wegar

是的,或者您也可以将mappingJackson2HttpMessageConverteras作为参数添加到按原样restTemplate使用的函数中。
mvogiatzis

1
您不必创建新的映射器,只要(mappingJackson2HttpMessageConverter的httpMessageConverter实例){((MappingJackson2HttpMessageConverter)httpMessageConverter).setObjectMapper(mapper());即可设置映射器。}
Kalpesh Soni,

在Java8中,您可以使用流,而在Java14中,可以将模式匹配用于instanceof。否则,很好的声明式解决方案。
安德烈·布迪

6

要完成其他答案:如果你ObjectMapper只是注册了杰克逊Module定制的串行/解串器,你可能想直接在现有注册模块ObjectMapperRestTemplate的默认MappingJackson2HttpMessageConverter如下(例如无DI但同样适用,如果使用DI):

    SimpleModule module = new SimpleModule();
    module.addSerializer(...);
    module.addDeserializer(...);

    MappingJackson2HttpMessageConverter messageConverter = restTemplate.getMessageConverters().stream()
                    .filter(MappingJackson2HttpMessageConverter.class::isInstance)
                    .map(MappingJackson2HttpMessageConverter.class::cast)
                    .findFirst().orElseThrow( () -> new RuntimeException("MappingJackson2HttpMessageConverter not found"));
    messageConverter.getObjectMapper().registerModule(module);

这样您就可以完成原始文件的配置ObjectMapper(如Spring的那样Jackson2ObjectMapperBuilder),而不是替换它。


6

使用Spring Boot,它很简单:

RestTemplate template = new RestTemplateBuilder()
                            .additionalMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper))
                            .build()

(使用Spring Boot 2.2.1进行测试)


使用这种方法,您RestTemplate将只有一个MessageConverterMappingJackson2HttpMessageConverter。但我仍然喜欢
站在

additionalMessageConverters并不是真正的添加剂。它将完全替换,您将失去其他转换器。它在其文档字符串中说明。(它将添加到构建器的转换器列表中,它将完全替换实例的转换器)
Ming
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.