Hartmut Lang
Hartmut Lang

Reputation: 41

Spring HATEOAS with Traverson client and java.time.Instant

Using spring hateoas 1.0.3 with a traverson client is causing problems when the rest-entity has an attribute of type "java.time.Instant". The error i get is

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.Instant`

I found that the HttpMessageConverter used inside of the RestTemplate in traverson has only the Jackson2HalModule registered.

Is there a way that i can also register the jackson-modules-java8 module in traverson?

Or is there a way that i can register the Jackson2HalModule in my restTemplate outside of traverson?

Upvotes: 0

Views: 477

Answers (2)

Andriy
Andriy

Reputation: 259

Following worked for me in spring-hateoas 1.1.2.RELEASE

private static RestTemplate getRestTemplate() {
    List<HttpMessageConverter<?>> httpMessageConverters = SpringFactoriesLoader.loadFactories(TraversonDefaults.class,
            Traverson.class.getClassLoader()).get(0).getHttpMessageConverters(Collections.singletonList(MediaTypes.HAL_JSON));
    Optional<HttpMessageConverter<?>> first = httpMessageConverters.stream().filter(i -> i instanceof MappingJackson2HttpMessageConverter)
            .findFirst();

    if (first.isPresent()) {
        MappingJackson2HttpMessageConverter httpMessageConverter = (MappingJackson2HttpMessageConverter) first.get();
        httpMessageConverter.getObjectMapper().registerModule(new JavaTimeModule());
    }

    return new RestTemplateBuilder().messageConverters(httpMessageConverters).build();
}

and then using it:

Traverson traverson = new Traverson(URI.create("http://localhost:8080"), MediaTypes.HAL_JSON);
    traverson.setRestOperations(getRestTemplate());

Upvotes: 2

Hartmut Lang
Hartmut Lang

Reputation: 41

After some investigation i found a solution that works for me. The background is that the traverson client registers a MappingJackson2HttpMessageConverter with contains the Jackson2HalModule. To fix the problem i had to register also the JavaTimeModule.

I did the following

RestTemplateBuilder genericBuilder = this.restTemplateBuilder
    .setConnectTimeout(Duration.ofMillis(configuration.getSecurityRestConnectTimeout()))
    .setReadTimeout(Duration.ofMillis(configuration.getSecurityRestReceiveTimeout()));

// my normal restTemplate
RestTemplateBuilder restTemplate = genericBuilder
                .defaultMessageConverters()
                .build();

// HAL specific restTemplate                
RestTemplateBuilder restTemplateHal = genericBuilder
                .messageConverters(getHalConverter(Arrays.asList(MediaTypes.HAL_JSON)))
                .build();

The HalConverter is generated like this (registering also JavaTimeModule):

   private static HttpMessageConverter<?> getHalConverter(List<MediaType> halFlavours) {

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Jackson2HalModule());
        mapper.registerModule(new JavaTimeModule());
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();

        converter.setObjectMapper(mapper);
        converter.setSupportedMediaTypes(halFlavours);

        return converter;
    }

Then you can use Traverson in setting the just generated restTemplateHal

    Traverson traverson = new Traverson(uri, MediaTypes.HAL_JSON);
    traverson.setRestOperations(restTemplateHal);
    MyClass myclass = traverson.follow().toObject(MyClass.class);

Upvotes: 0

Related Questions