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

问题描述 投票:0回答:7

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

spring jackson
7个回答
108
投票

默认的

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 - 我必须在 XML 中显式创建

messageConverters
列表,而使用
@Configuration
方法我可以引用现有列表并简单地对其进行修改。但这应该有效。


35
投票

如果您不使用 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);

15
投票

RestTemplate 将初始化其默认消息转换器。您应该将

MappingJackson2HttpMessageConverter
替换为您自己的 bean,该 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
}

13
投票

要完成其他答案:如果您的

ObjectMapper
只是使用自定义序列化器/反序列化器注册 Jackson
Module
,您可能需要直接在现有
ObjectMapper
上从
RestTemplate
的默认
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
所做的那样),而不是替换它。


10
投票

使用 Spring Boot,就这么简单:

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

(使用 Spring Boot 2.2.1 测试)


1
投票

仅使用当前的 ObjectMapper 更新 SerializationConfig.Feature:

RestTemplate restTemplate =  new RestTemplate();
restTemplate.getMessageConverters().forEach(mc -> {
            if (mc instanceof MappingJackson2HttpMessageConverter ) {
                //whatever you want to change
                ((MappingJackson2HttpMessageConverter) mc).getObjectMapper().setSerializationInclusion(Include.NON_NULL);
            }
        });

0
投票

我写了一个单元测试来回答这个问题。

  • RestTemplate 对象被构建为具有默认转换器。
  • removeIf 用于从默认转换器列表中删除 MappingJackson2HttpMessageConverter 类型的转换器。
  • CustomMappingJackson2HttpMessageConverter 已添加到列表中。
  • 用于检查列表是否不再有基本类型 MappingJackson2HttpMessageConverter 的转换器,而是新的子类型 CustomMappingJackson2HttpMessageConverter(类型为 MappingJackson2HttpMessageConverter)的断言。
  • 列表中没有重复项,并且此列表仍然有 CustomMappingJackson2HttpMessageConverter,它也是一个 MappingJackson2HttpMessageConverter。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>be.mteam</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>   
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>       
</project>

@Test
void buildRestTemplateWithReplacedMappingJackson2HttpMessageConverter() {

    // a RestTemplateBuilder is used to build a RestTemplate
    // having the default message converters 

    RestTemplateBuilder builder = new RestTemplateBuilder()
            .defaultMessageConverters();

    RestTemplate restTemplate = builder.build();

    // nothing special, the template is built, 5 default converters
    // 0 = {ByteArrayHttpMessageConverter@2724} 
    // 1 = {StringHttpMessageConverter@2725} 
    // 2 = {ResourceHttpMessageConverter@2726} 
    // 3 = {AllEncompassingFormHttpMessageConverter@2727} 
    // 4 = {Jaxb2RootElementHttpMessageConverter@2728} 
    // 5 = {MappingJackson2HttpMessageConverter@2729} 

    List<HttpMessageConverter<?>> converters = new ArrayList<>(restTemplate.getMessageConverters());

    // using removeIf, MappingJackson2HttpMessageConverter is removed
    converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);

    // the desired CustomMappingJackson2HttpMessageConverter of type MappingJackson2HttpMessageConverter is added to the list of converters       
    converters.add(new CustomMappingJackson2HttpMessageConverter());

    // the converters are put back into the object restTemplate
    restTemplate.setMessageConverters(converters);

    List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();

    boolean containsDefaultJackson = messageConverters.stream()
            .anyMatch(converter -> converter instanceof MappingJackson2HttpMessageConverter &&
                    !(converter instanceof CustomMappingJackson2HttpMessageConverter));

    boolean containsCustomJackson = messageConverters.stream()
            .anyMatch(converter -> converter instanceof CustomMappingJackson2HttpMessageConverter);

    // Assert that the default MappingJackson2HttpMessageConverter was replaced
    assertThat(containsDefaultJackson).isFalse();

    // Assert that the customized MappingJackson2HttpMessageConverter was added
    assertThat(containsCustomJackson).isTrue();

    // Verify the order: custom converter should replace the default one
    assertThat(messageConverters).doesNotHaveDuplicates();

    assertThat(messageConverters.stream()
            .filter(converter -> converter instanceof MappingJackson2HttpMessageConverter)
            .count()).isEqualTo(1);
}

© www.soinside.com 2019 - 2024. All rights reserved.