Can't set ProblemHandler to ObjectMapper in Spring Boot

后端 未结 2 1983
谎友^
谎友^ 2020-12-06 13:46

I tried to add custom problem handler to object mapper with Jackson2ObjectMapperBuilderCustomizer:

@Bean
public Jackson2ObjectMapperBuilderCustomizer customi         


        
相关标签:
2条回答
  • 2020-12-06 14:26

    I am new to Spring Boot, then, it was difficult to understand how I could use this. But after some research I managed to figure out.

    I had to create a class named src.main.java.com.applicationname.config.JacksonUnknownPropertyConfig.java with the contents:

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

    And I also had to add FAIL_ON_UNKNOWN_PROPERTIES to the file src.main.resources.application.properties

    spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=true
    

    After this, looks like Sprint Boot automatically recognizes the new class I created and correctly loads it.

    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
    

    Related threads:

    1. Spring Boot, Spring MVC JSON RequestBody: Unknown property ignored
    2. How to customise Jackson in Spring Boot 1.4
    3. jackson Unrecognized field
    4. Ignoring new fields on JSON objects using Jackson
    5. Configure FAIL_ON_UNKNOWN_PROPERTIES for each RequestMapping differently in the Controller
    6. Jackson deserializer priority?
    7. Spring Data Elastic - Java.Time.Instant class Jackson deserliization not working
    8. Enable Jackson Deserialization of Empty Objects to Null
    9. Spring RestController : reject request with unknown fields
    10. How do you globally set Jackson to ignore unknown properties within Spring?
    11. https://www.baeldung.com/spring-bean
    12. https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-spring-mvc
    0 讨论(0)
  • 2020-12-06 14:29

    It's not possible to directly add a DeserializationProblemHandler to the ObjectMapper via a Jackson2ObjectMapperBuilder or Jackson2ObjectMapperBuilderCustomizer. Calling build() on the builder is a no-go, since the resulting ObjectMapper is local to the method: Spring itself will call build() later, creating another ObjectMapper instance.

    However, it's possible to do so by registering a Jackson module :

    • the builder has a modules() method
    • the module has access via setupModule() to a SetupContext instance, which has a addDeserializationProblemHandler() method

    This should then work

    @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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题