0

In a Spring application I want to use my own JsonSerializer with a RestController. My JsonSerializer is registered in a Jackson2ObjectMapperBuilder. So far so good, but when I annotate the respective fields with @JsonSerialize Spring MVC complains that there is no converter found for my class. Here is the code:

public class DurationAsHourSerializer extends JsonSerializer<Duration>{
    @Override
    public void serialize(Duration value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException, JsonProcessingException {
            gen.writeNumber(value.toHours());

    }
}

@Configuration
@EnableWebMvc
public class AppConfig {
    @Bean
    public Jackson2ObjectMapperBuilder jacksonBuilder() {
        Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
        SimpleModule module = new SimpleModule();
        module.addSerializer(Duration.class, new DurationAsHourSerializer());
        b.modulesToInstall(module);
        return b;
    }
}

@EqualsAndHashCode
@ToString
public class Order {
    @Getter
    @Setter
    private String id;

    @Getter
    @Setter
    @NotNull
    private String customerId;

    @Getter
    @Setter
    private ZonedDateTime validFrom;

    @Getter
    @Setter
    private ZonedDateTime validTo;

    @Getter
    @Setter
    @JsonSerialize(as=DurationAsHourSerializer.class)
    private Duration maxDuration;

    @Getter
    @Setter
    @JsonSerialize(as=DurationAsHourSerializer.class)
    private Duration actDuration;
}

@RestController
@RequestMapping("order")
public class OrderController {

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<?> createOrder(@RequestBody @Valid Order order) {
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(ServletUriComponentsBuilder.fromCurrentRequest().path("/" + order.getId()).build().toUri());
        return new ResponseEntity<>(null, headers, HttpStatus.CREATED);
    }

    @RequestMapping(method = RequestMethod.GET)
    public Order getExample() {
        Order order = new Order();
        order.setMaxDuration(Duration.ofHours(10));
        return order;
    }
}

When I send a request to getExample(), I get the following error:

java.lang.IllegalArgumentException: No converter found for return value of type: class com.sap.sptutorial.rest.Order at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:178) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]

Why can't the Converter use my serializer, or why does my serializer make the otherwise existing converter unavailable?

PS: This is a follow up to How do I use a custom Serializer with Jackson? where I tried to use a Field Formatter for the same job, and learned that Formatters are not used for the Jackson Serialization.

Community
  • 1
  • 1
Gregor
  • 2,917
  • 5
  • 28
  • 50

2 Answers2

0

It reason behind this could be, because there is already another object of Jackson2ObjectMapperBuilder constructed by Spring and it is still using it.

You need to make sure you make this @Bean as the default to be used by spring or use @PostConstruct rather that @Bean to modify the same instance of Jackson2ObjectMapperBuilder initialized by Spring

@Autowired
private Jackson2ObjectMapperBuilder builder;
............

@PostConstruct
public Jackson2ObjectMapperBuilder jacksonBuilder() {
    Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
    SimpleModule module = new SimpleModule();
    module.addSerializer(Duration.class, new DurationAsHourSerializer());
    b.modulesToInstall(module);
    return b;
}
Hbargujar
  • 360
  • 2
  • 4
  • 14
  • No. If I remove the JsonSerialize annotation from the Duration typed fields the converter is found! The serializer seems to be registered correctly. But its use somehow makes the otherwise used converter unavailable. – Gregor Feb 17 '16 at 07:22
0

The error is that I used the wrong attribute with the @JsonSerialize annotation. Instead of @JsonSerialize(as=DurationAsHourSerializer.class) you have to declare the serializer to use with @JsonSerialize(using=DurationAsHourSerializer.class)

Gregor
  • 2,917
  • 5
  • 28
  • 50