web-dev-qa-db-fra.com

Comment pouvons-nous configurer le mappeur de Jackson interne lors de l'utilisation de RestTemplate?

Je souhaite mettre à jour les propriétés SerializationConfig.Feature ... du mappeur Jackson utilisé par Spring RestTemplate. Toute idée de la façon dont je peux y accéder ou de l'endroit où je peux/dois le configurer.

65
Usman Ismail

Le constructeur par défaut RestTemplate enregistre un ensemble de 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());
}

La MappingJacksonHttpMessageConverter à son tour crée l'instance ObjectMapper directement. Vous pouvez trouver ce convertisseur et remplacer ObjectMapper ou en enregistrer un nouveau avant celui-ci. Cela devrait fonctionner:

@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
}

En XML, cela ressemble à ceci:

<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"/>

Notez que la transition n’est pas vraiment 1: 1 - je dois explicitement créer messageConverters list en XML alors que avec @Configuration _ Je pouvais référencer un existant et le modifier simplement. Mais ça devrait marcher.

83
Tomasz Nurkiewicz

Si vous n'utilisez pas Spring IOC, vous pouvez faire quelque chose comme ceci (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);
22
Matt Sidesinger

RestTemplate initialisera ses convertisseurs de messages par défaut. Vous devriez remplacer le MappingJackson2HttpMessageConverter avec votre propre haricot, qui doit utiliser le mappeur d'objets que vous souhaitez utiliser. Cela a fonctionné pour moi:

@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
}
11
mvogiatzis

Pour compléter les autres réponses: si votre ObjectMapper enregistre simplement un Jackson Module avec des sérialiseurs/désérialiseurs personnalisés, vous voudrez peut-être enregistrer votre module directement sur le ObjectMapper existant à partir de RestTemplate par défaut MappingJackson2HttpMessageConverter comme suit (exemple sans DI mais la même chose s'applique si vous utilisez 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);

Cela vous permettra de terminer la configuration de l'original ObjectMapper (comme l'a fait Spring Jackson2ObjectMapperBuilder) au lieu de le remplacer.

6