Настройте обработчик проблем десериализации Джексона в среде Spring

Как я понял, Spring уже предоставляет bean-компонент для Jackson ObjectMapper. Поэтому вместо создания нового bean-компонента я пытаюсь настроить этот bean-компонент.

Из этого сообщения в блоге и затем этот проект Github Я использовал Jackson2ObjectMapperBuilder bean для достижения этой настройки.

@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(ApplicationContext context) {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.findModulesViaServiceLoader(true);
    return builder;
}

Затем я пытался настроить десериализатор, чтобы сделать его более мягким: если при десериализации свойства возникает исключение, я хочу, чтобы свойство объекта результата было null, и чтобы десериализация продолжалась (по умолчанию происходит сбой для первого свойства, которое не может быть десериализованным).

Мне удалось добиться этого с помощью класса NullableFieldsDeserializationProblemHandler, который расширяет DeserializationProblemHandler (я не думаю, что код актуален, но при необходимости я могу поделиться им).

Самый простой способ зарегистрировать этот обработчик - использовать .addHandler() метод ObjectMapper. Но, конечно, мне нужно будет устанавливать это каждый раз, когда я ввожу и использую ObjectMapper. Я хотел бы иметь возможность настроить обработчик так, чтобы каждый раз, когда ObjectMapper подключается автоматически, обработчик уже присутствовал.

Лучшее решение, которое я придумал до сих пор, - использовать аннотацию @PostConstruct только для регистрации обработчика проблемы.

@Configuration
public class JacksonConfiguration implements InitializingBean {

  @Autowired private ObjectMapper objectMapper;

  @Bean
  public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(ApplicationContext context) {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.findModulesViaServiceLoader(true);
    return builder;
  }

  @Override
  public void afterPropertiesSet() {
    objectMapper.addHandler(new NullableFieldsDeserializationProblemHandler());
  }
}

Но проблема этого решения в том, что, похоже, я все еще могу получить доступ к ObjectMapper с автоматическим подключением, который еще не зарегистрировал обработчик проблемы (я вижу, что это происходит после того, как он мне понадобится в режиме отладки).

Есть идеи, как мне зарегистрировать этот обработчик? Я заметил, что у Jackson2ObjectMapperBuilder есть .handlerInstantiator(), но я не мог понять, как его использовать.

Примечание. Я также пробовал использовать Jackson2ObjectMapperBuilderCustomizer, поскольку я использую Spring Boot, но не получил лучших результатов.


person woshilapin    schedule 13.02.2018    source источник


Ответы (2)


Невозможно напрямую добавить DeserializationProblemHandler к ObjectMapper через Jackson2ObjectMapperBuilder или Jackson2ObjectMapperBuilderCustomizer. Метод handlerInstanciator() предназначен для чего-то другого.

Однако это можно сделать, зарегистрировав модуль Джексона:

  • у строителя есть modules() метод
  • модуль имеет доступ через setupModule() к экземпляру SetupContext, у которого есть метод addDeserializationProblemHandler()

Это работает:

@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
    return new Jackson2ObjectMapperBuilderCustomizer() {
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) {
            builder.modules(new MyModule());
        }
    };
}

private static class MyModule extends SimpleModule {
    @Override
    public void setupModule(SetupContext context) {
        // Required, as documented in the Javadoc of SimpleModule
        super.setupModule(context);
        context.addDeserializationProblemHandler(new NullableFieldsDeserializationProblemHandler());
    } 
}
person Frank Pavageau    schedule 26.02.2018
comment
Это действительно помогает, и я не уверен, что бы произошло, если бы мне пришлось зарегистрировать несколько модулей. Стоит ли регистрировать обработчик проблем на каждом из них? А как насчет тех, которые автоматически регистрируются через findModulesViaServiceLoader(true)? Что ж, это вызывает еще несколько вопросов, но я считаю, что исходный вопрос решен. Спасибо :-) - person woshilapin; 27.02.2018
comment
Модули - это способ настроить общий ObjectMapper, на котором заканчивается DeserializationProblemHandler: SetupContext - это оболочка вокруг ObjectMapper, не Module. Таким образом, нет проблем с несколькими модулями, если они не пытаются настроить разные обработчики (обычно они и не делают этого). - person Frank Pavageau; 28.02.2018
comment
Подумайте о том, чтобы принять ответ, если он, кстати, решит проблему :) - person Frank Pavageau; 28.02.2018
comment
Привет, я пробовал это решение, но поток кода не попадает внутрь handleUnknownProperty. Я использую код, указанный в этом ответе. Я что-нибудь упускаю? - person vizsatiz; 13.03.2019

А как насчет написания такого bean-компонента:

@Configuration
public class ObjectMapperConfiguration {

    @Bean
    ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // jackson 1.9 and before
        objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); 
        // or jackson 2.0
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
}

Это для глобальной конфигурации. Если вместо этого вы хотите настроить функцию для определенного класса, используйте эту аннотацию над определением класса:

@JsonIgnoreProperties(ignoreUnknown = true)
person NiVeR    schedule 13.02.2018
comment
Что ж, весь смысл настройки bean-компонента заключается в настройке, а не в создании нового. Я хотел бы настроить экземпляр Spring, чтобы получить все модификации / настройки, которые Spring уже сделал для ObjectMapper. - person woshilapin; 13.02.2018
comment
Это one instance, который Spring инициализируется один раз и будет использоваться во всем приложении. - person NiVeR; 14.02.2018
comment
Да, конечно, это фасоль. Я хочу сказать, что Spring уже предоставляет этот компонент. Если я создам один, у меня будет два, и Spring может случайным образом выбрать один. Кроме того, тот, который уже предоставлен, может быть уже инициализирован и / или настроен, и я хотел бы сохранить эти настройки. - person woshilapin; 14.02.2018