Search code examples
javaspringjacksondeserializationfasterxml

Spring Boot register custom jackson (polygon) deserializer after Spring Boot upgrade 2.4->2.5


TLDR; How can I tell spring to use my custom deserializer? (Yes, I checked but it's fubar)

I have a PostMapping equipped with a Polygon as RequestBody param; It takes a array of arrays (containing points) and should convert them to a org.elasticsearch.geometry.Polygon

@PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<SearchDTO>> findSpatialResults(@RequestBody Polygon polygon) {...}

As this need a custom deserializer, this is it;

public class PolygonDeserializer extends StdDeserializer<Polygon> {

    public PolygonDeserializer() {
        super(Polygon.class);
    }

    @Override
    public Polygon deserialize(JsonParser jp, DeserializationContext context)
            throws IOException {
        ObjectCodec oc = jp.getCodec();
        JsonNode node = oc.readTree(jp);
      ...
    }
}

Which is registered in a @Configuration annotated SpringConfig class;

@Configuration
public class SpringConfig {

    @Bean
    public Module polygonDeserializer() {
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Polygon.class, new PolygonDeserializer());
        return module;
    }
}

Before updating Spring this worked, now I get a JSON parse error: Cannot deserialize instance of `org.elasticsearch.geometry.Polygon` out of START_ARRAY token and can't figure out why.


Solution

  • I was using two @configuration annotated classes.

    Fixed by adding the first config (WebConfig.class) into SpringConfig.class and registering it as a Bean;

    @Configuration
    public class SpringConfig {
    
        @Bean
        public ModelMapper modelMapper() {
            return new ModelMapper();
        }
    
        @Bean
        public WebConfig webConfig() {
            return new WebConfig();
        }
    
        @Bean
        public Module polygonDeserializer() {
            SimpleModule module = new SimpleModule();
            module.addDeserializer(Polygon.class, new PolygonDeserializer());
            return module;
        }
    }
    

    Second config before change;

    @EnableWebMvc
    @Configuration //<---- **Issue**
    public class WebConfig implements WebMvcConfigurer {
    
        ...
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
        ...
        }
    }
    

    Spring calls only one of the two config classes and does not register the custom deserializer.