Невозможно установить для ProblemHandler значение ObjectMapper в Spring Boot

Я попытался добавить собственный обработчик проблем в сопоставитель объектов с помощью Jackson2ObjectMapperBuilderCustomizer:

@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
    return new Jackson2ObjectMapperBuilderCustomizer() {
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) {
            ObjectMapper m = builder.build();
            m.addHandler(
                    new DeserializationProblemHandler() {
                        @Override
                        public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName) throws IOException {
                            System.out.println("ahahahaa");
                            return super.handleUnknownProperty(ctxt, p, deserializer, beanOrClass, propertyName);
                        }
                    }
            );
        }
    };
}

Но когда я автоматически подключил свойство bean-компонента ObjectMapper _problemHandlers, оно равно null.

Я также попытался просто настроить существующий ObjectMapper с помощью:

@Autowired
public customize(ObjectMapper mapper) {
...
}

Но результат тот же. Я не знаю, кто может стереть это свойство. Я вообще не инициализирую другие строители/фабрики/и т. д. объекта сопоставления в другом месте. Что я делаю неправильно?


person DamienMiheev    schedule 09.10.2017    source источник


Ответы (2)


Невозможно напрямую добавить DeserializationProblemHandler к ObjectMapper через Jackson2ObjectMapperBuilder или Jackson2ObjectMapperBuilderCustomizer. Вызов build() для построителя невозможен, так как результирующий ObjectMapper является локальным для метода: Spring сам вызовет build() позже, создав еще один экземпляр ObjectMapper.

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

  • у строителя есть метод 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 MyDeserializationProblemHandler());
    } 
}

private static class MyDeserializationProblemHandler extends DeserializationProblemHandler {
    @Override
    public boolean handleUnknownProperty(DeserializationContext ctxt,
                                         JsonParser p,
                                         JsonDeserializer<?> deserializer,
                                         Object beanOrClass,
                                         String propertyName)
            throws IOException {
        System.out.println("ahahahaa");
        return super.handleUnknownProperty(ctxt, p, deserializer, beanOrClass, propertyName);
    }
}
person Frank Pavageau    schedule 13.02.2018
comment
Привет, я использовал твой код. Но управление не поступает внутрь handleUnknownProperty, когда в моем запросе есть недопустимое поле JSON. Я что-то упустил здесь? - person vizsatiz; 13.03.2019
comment
context.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) может быть? Это зависит от того, что он делает вместо этого. - person Frank Pavageau; 13.03.2019

Я новичок в Spring Boot, тогда мне было сложно понять, как я могу это использовать. Но после некоторых исследований мне удалось выяснить.

Мне пришлось создать класс с именем src.main.java.com.applicationname.config.JacksonUnknownPropertyConfig.java с содержимым:

package com.applicationname.config;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;

@SpringBootConfiguration
public class JacksonUnknownPropertyConfig {
  private static final Logger logger = LoggerFactory.getLogger(JacksonUnknownPropertyConfig.class);

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

  private static class MyDeserializationProblemHandler extends DeserializationProblemHandler {
    @Override
    public boolean handleUnknownProperty(
        DeserializationContext ctxt,
        JsonParser p,
        JsonDeserializer<?> deserializer,
        Object beanOrClass,
        String propertyName)
        throws IOException {

      System.out.println("ahahahaa");

      final String missing = String.format("Unknown request property '%s'", propertyName);
      logger.warn(missing);

      if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, missing);
      }
      return super.handleUnknownProperty(ctxt, p, deserializer, beanOrClass, propertyName);
    }
  }
}

А еще мне пришлось добавить FAIL_ON_UNKNOWN_PROPERTIES в файл src.main.resources.application.properties

spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=true

После этого, похоже, что Sprint Boot автоматически распознает созданный мной новый класс и корректно загружает его.

2020-07-20 23:41:22.934  INFO 16684 --- [         task-1] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-07-20 23:41:22.944 TRACE 16684 --- [         task-1] o.h.type.spi.TypeConfiguration$Scope     : Handling #sessionFactoryCreated from [org.hibernate.internal.SessionFactoryImpl@3edd0926] for TypeConfiguration
2020-07-20 23:41:22.946  INFO 16684 --- [         task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-07-20 23:41:23.209  INFO 16684 --- [           main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-07-20 23:41:23.222  INFO 16684 --- [           main] c.a.p.AppointmentPublishingApplication   : Started AppointmentPublishingApplication in 6.445 seconds (JVM running for 7.615)
2020-07-20 23:41:26.229  INFO 16684 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-07-20 23:41:26.229  INFO 16684 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-07-20 23:41:26.236  INFO 16684 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 7 ms
ahahahaa

Связанные темы:

  1. Spring Boot, Spring MVC JSON RequestBody: неизвестное свойство игнорируется
  2. Как настроить Джексона в Spring Boot 1.4
  3. Неопознанное поле Джексона
  4. Игнорирование новых полей в объектах JSON с помощью Джексона
  5. Настройте FAIL_ON_UNKNOWN_PROPERTIES для каждого RequestMapping по-разному в контроллере< /а>
  6. приоритет десериализатора Джексона?
  7. Spring Data Elastic — класс Java.Time.Instant Десерлизация Джексона не работает
  8. Включить десериализацию Джексоном пустых объектов в null
  9. Spring RestController: отклонить запрос с неизвестными полями
  10. Как глобально настроить Джексона на игнорирование неизвестные свойства в Spring?
  11. https://www.baeldung.com/spring-bean
  12. https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-spring-mvc
person user    schedule 21.07.2020