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

Posted

技术标签:

【中文标题】使用 RestTemplate 时如何配置内部 Jackson 映射器?【英文标题】:How can we configure the internal Jackson mapper when using RestTemplate? 【发布时间】:2012-03-12 00:08:19 【问题描述】:

我想更新 Spring RestTemplate 使用的 jackson 映射器的 SerializationConfig.Feature... 属性,知道如何获取它或我可以/应该在哪里配置它。

【问题讨论】:

【参考方案1】:

默认的RestTemplate构造函数注册了一组HttpMessageConverters:

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 - 我必须在 XML 中显式创建 messageConverters 列表,而使用 @Configuration 方法我可以引用现有的并简单地修改它。但这应该可行。

【讨论】:

如果您能将其翻译成 XML,我将不胜感激。真的很想试试这个,但不知道如何配置它。非常感谢! 不确定这是否只是由于版本差异,但我相信 Jackson 转换器类的正确名称是 MappingJackson*2*HttpMessageConverter 我可能会避免在现有默认值的开头添加新的 mappingJacksonHttpMessageConverter,而是替换现有的 Jackson 消息转换器。否则,您最终会在列表中拥有 2 个 Jackson 消息转换器。 我尝试在索引 0 处添加它,但我得到了一些非常奇怪的 JSON 解析异常。像@mvogiatzis 建议的那样删除现有的效果更好 我需要在 @Bean 定义中更改 RestOperations -&gt; RestTemplate 以使其正常工作【参考方案2】:

如果你没有使用 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);

【讨论】:

当你发现它在迭代时,为什么你需要删除并重新添加? restTemplate.getMessageConverters().removeIf(m -> m.getClass().isAssignableFrom(MappingJackson2HttpMessageConverter.class));【参考方案3】:

RestTemplate 将初始化其默认消息转换器。你应该用你自己的bean替换MappingJackson2HttpMessageConverter,它应该使用你想使用的对象映射器。这对我有用:

@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 这行得通,但似乎答案中有错字? restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter); 应该是 restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter());(注意 mappingJackson2HttpMessageConverter() 中的括号,使其成为方法调用而不是引用) True,或者您也可以将mappingJackson2HttpMessageConverter 作为参数添加到restTemplate 函数,按原样使用。 你不必创建一个新的,你可以设置你的映射器 if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) ((MappingJackson2HttpMessageConverter)httpMessageConverter).setObjectMapper(mapper()); 在 Java8 中您可以使用流或 Java14 您可以对 instanceof 使用模式匹配。否则很好的声明性解决方案。【参考方案4】:

要完成其他答案:如果您的 ObjectMapper 只是使用自定义序列化器/反序列化器注册了 Jackson Module,您可能希望从 RestTemplate 的默认 @987654325 直接在现有 ObjectMapper 上注册您的模块@ 如下(不使用 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 所做的那样),而不是替换它。

【讨论】:

【参考方案5】:

使用Spring Boot,就这么简单:

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

(使用 Spring Boot 2.2.1 测试)

【讨论】:

使用这种方法,您的RestTemplate 将只有一个MessageConverterMappingJackson2HttpMessageConverter。但我还是喜欢它 additionalMessageConverters 并不是真正的添加剂。它将执行完全替换,您将丢失其他转换器。它在其文档字符串中说明。 (它会添加到构建器的转换器列表中,这将完全替换实例的)

以上是关于使用 RestTemplate 时如何配置内部 Jackson 映射器?的主要内容,如果未能解决你的问题,请参考以下文章

使用 RestTemplate 测试 ExceptionHandler

模拟 mock<RestTemplate> getForObject 时如何解决歧义?

使用resttemplate访问时,如何使端点仅接受springboot中启用@crossorigin的uri?为啥我没有收到 cors 错误? [复制]

如何使用@WebMvcTest 春季测试在模拟服务中注入模拟的restTemplate

如何使用注释自动装配 RestTemplate

RestTemplate配置