Як ми можемо налаштувати внутрішній маппер Джексона під час використання RestTemplate?


78

Я хочу оновити властивості SerializationConfig.Feature ... властивостей mappers jackson, використовуваних Spring RestTemplate, будь-яка ідея, як я можу до нього дістатися або де я можу / повинен це налаштувати.

Відповіді:


97

Конструктор за замовчуванням 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 - я повинен чітко створити messageConvertersсписок у XML, тоді як за допомогою @Configurationпідходу я міг би посилатися на існуючий і просто модифікувати його. Але це має спрацювати.


Буду вдячний, якщо ви зможете перекласти це у XML. дуже хочу спробувати це, але не знаю, як його налаштувати. дуже дякую!
Джастін

9
Не впевнений, що це пов’язано лише з різницею у версії, але я вважаю, що правильною назвою для класу конвертера Джексона є MappingJackson * 2 * HttpMessageConverter
nbrooks

2
Можливо, я б уникнув додавання нового mappingJacksonHttpMessageConverter на початку існуючих за замовчуванням, але замість цього замінював існуючий конвертер повідомлень Джексона. В іншому випадку у вас буде 2 перетворювача повідомлень Джексона у списку.
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
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.
Герман

1
Це спрацювало, але, здається, у відповіді є помилка? restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter);має бути restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter());(зверніть увагу на дужки в mappingJackson2HttpMessageConverter (), зробивши це викликом методу замість посилання)
Jens Wegar

Правда, або ви можете альтернативно додати параметр mappingJackson2HttpMessageConverterяк параметр до restTemplateфункції, використовуйте його як є.
mvogiatzis

1
вам не потрібно створювати новий, ви можете просто встановити свій mapper, якщо (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {((MappingJackson2HttpMessageConverter) httpMessageConverter) .setObjectMapper (mapper ()); }
Калпеш Соні

У Java8 ви можете використовувати потоки або Java14 ви можете використовувати відповідність шаблонів для instanceof. В іншому випадку приємне декларативне рішення.
Андрій Будай

6

Для того, щоб заповнити інші відповіді: якщо ваші ObjectMapperпросто реєструє Джексон Moduleдо призначених для користувача серіалізатор / deserializers, ви можете зареєструвати ваш модуль безпосередньо на існуючому ObjectMapperз RestTemplate«s по замовчуванням 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's Jackson2ObjectMapperBuilder), замість того, щоб замінювати його.


6

Використовуючи Spring Boot, це так просто, як:

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

(Випробувано Spring Boot 2.2.1)


При такому підході ваш RestTemplateматиме тільки одне MessageConverter: MappingJackson2HttpMessageConverter. Але мені все одно подобається
стояти самостійно

additionalMessageConvertersнасправді не є добавкою. Він виконає повну заміну, і ви втратите інші перетворювачі. Це зазначено в його документі. (Це додає до списку конвертерів розробника, який повністю замінить екземпляри)
Ming
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.