83 votes

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

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

102voto

Tomasz Nurkiewicz Points 140462

La valeur par défaut RestTemplate enregistre un ensemble de HttpMessageConverter s :

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

Le site MappingJacksonHttpMessageConverter à son tour, crée ObjectMapper instance directement. Vous pouvez soit trouver ce convertisseur et remplacer ObjectMapper ou d'en enregistrer un nouveau avant lui. 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, c'est quelque chose comme ça :

<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 en XML alors qu'avec @Configuration Je pourrais faire référence à une approche existante et la modifier simplement. Mais cela devrait fonctionner.

30voto

Matt Sidesinger Points 952

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

1 votes

Pourquoi faut-il supprimer et réinsérer quand on peut simplement le trouver en itérant ?

1 votes

RestTemplate.getMessageConverters().removeIf(m -> m.getClass().isAssignableFrom(MappingJackson2HttpMessageConverter.class)) ;

14voto

mvogiatzis Points 133

RestTemplate va initialiser ses convertisseurs de messages par défaut. Vous devez remplacer le MappingJackson2HttpMessageConverter avec votre propre bean, qui devrait utiliser le mappeur d'objets que vous voulez utiliser. Ceci 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
}

0 votes

Il semble qu'il soit important de nommer la méthode comme suit myObjectMapper() mais pas seulement objectMapper() . Pour une raison quelconque, il semble que la méthode ne soit jamais appelée et que vous obteniez la valeur par défaut. ObjectMapper .

1 votes

Cela a fonctionné, mais il semble qu'il y ait une faute de frappe dans la réponse ? restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter); devrait être restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter()); (notez la parenthèse dans mappingJackson2HttpMessageConverter(), ce qui en fait un appel de méthode au lieu d'une référence)

0 votes

Vrai, ou vous pouvez alternativement ajouter l'option mappingJackson2HttpMessageConverter en tant que paramètre de la restTemplate fonction l'utiliser telle quelle.

9voto

aliaksei Points 111

Pour compléter les autres réponses : si votre ObjectMapper enregistre juste un Jackson Module avec des sérialiseurs/désérialiseurs personnalisés, vous voudrez peut-être enregistrer votre module directement sur l'interface existante de l'entreprise. ObjectMapper 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 d'achever la configuration de l'appareil d'origine. ObjectMapper (comme l'a fait le programme de Spring Jackson2ObjectMapperBuilder ), au lieu de le remplacer.

8voto

Wim Deblauwe Points 2570

Avec Spring Boot, c'est aussi simple que cela :

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

(Testé avec Spring Boot 2.2.1)

0 votes

Avec cette approche, votre RestTemplate n'aura qu'un seul MessageConverter : MappingJackson2HttpMessageConverter . Mais je l'aime toujours

0 votes

additionalMessageConverters n'est pas vraiment additif. Il effectuera un remplacement complet et vous perdrez d'autres convertisseurs. C'est indiqué dans sa docstring. (Il ajoute à la liste de convertisseurs du constructeur, qui remplacera entièrement celui de l'instance).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X