lbenedetto
lbenedetto

Reputation: 2114

AnnotationIntrospector is not called if my custom annotation is present and the object mapper is being used by a RestTemplate

I am completely flabbergasted by this behavior.

I've configured a RestTemplate with a Jackson ObjectMapper that has a JacksonAnnotationIntrospector.

objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
      @Override
      public Object findDeserializer(Annotated a) {

I have a response entity I need to deserialize, and it needs my custom annotation on one of its fields to work.

When MyEntity is deserialized, findDeserializer is never called, and deserialization fails. If I remove my custom annotation from MyEntityOne, findDeserializer IS called, but since the annotation isn't present anymore, deserialization still fails. But this is only the case if the annotation is on a field that is present in the json being deserialized. If I put the annotation on some made-up field that is not in json, then findDeserializer is called.

Stranger still, if I instead ask the RestTemplate to give me back a String, and I manually pass that to my configured ObjectMapper, then it works as expected and can deserialize respecting my custom annotation.

So this fails

restTemplate.exchange(
          new URI(requestUrl),
          HttpMethod.GET,
          new HttpEntity<>(getHeaders()),
          MyEntity.class
);

But this works

var response = restTemplate.exchange(
          new URI(requestUrl),
          HttpMethod.GET,
          new HttpEntity<>(getHeaders()),
          String.class
).getBody();
objectMapper.readValue(response, MyEntity.class)

objectMapper is the same one given to the RestTemplate via RestTemplateBuilder

builder.additionalMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper))

Here's my custom annotation that marks a field as needing to use my custom deserializer

@Target(
    {ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EscapedJsonArray {
  Class<?> value();
}

Upvotes: 0

Views: 999

Answers (1)

lbenedetto
lbenedetto

Reputation: 2114

The issue was that the RestTemplate had multiple ObjectMappers. I still have no explanation for why it would choose its own ObjectMapper instead of the one I supplied in some circumstances but not others, but I resolved the issue by registering my ObjectMapper like this instead:

var restTemplate = restTemplateBuilder.build();
restTemplate.getMessageConverters().removeIf(converter -> converter.getClass().equals(MappingJackson2HttpMessageConverter.class));
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter(objectMapper));

Upvotes: 0

Related Questions