Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializer/Deserializer for Vavr objects

Hello Im trying to add vavr to my projet, right now Im struggling with proper serializaition of Vavr.List objects. Below is my controller:

import io.vavr.collection.List;

 @GetMapping(value = "/xxx")
    public List<EntityDeleted> getFile() {
        return List.of(new EntityDeleted(true),new EntityDeleted(true),new EntityDeleted(true),new EntityDeleted(true));
}

EntityDeleted is my custom object, List is Vavr collection as shown in import statement. The response Im getting in Postman is:

{
    "empty": false,
    "lazy": false,
    "async": false,
    "traversableAgain": true,
    "sequential": true,
    "singleValued": false,
    "distinct": false,
    "ordered": false,
    "orNull": {
        "deleted": true
    },
    "memoized": false
}

where I expect JSON list of my objects. Below is my config:

@SpringBootApplication
public class PlomberApplication {

    public static void main(String[] args) {
        SpringApplication.run(PlomberApplication.class, args);
    }

    @Bean
    public ObjectMapper jacksonBuilder() {
        ObjectMapper mapper = new ObjectMapper();
        return mapper.registerModule(new VavrModule());
    }
}

and bit of pom.xml

 <dependency>
            <groupId>io.vavr</groupId>
            <artifactId>vavr</artifactId>
            <version>0.9.0</version>
        </dependency>
        <dependency>
            <groupId>io.vavr</groupId>
            <artifactId>vavr-jackson</artifactId>
            <version>0.9.0</version>
        </dependency>
like image 287
filemonczyk Avatar asked Sep 18 '17 18:09

filemonczyk


3 Answers

Spring Boot retrieves all instances of com.fasterxml.jackson.databind.Module class and initializes ObjectMapper with them. There's no need for additional magic.

My dependencies are as follows (Spring Boot 1.5.7.RELEASE):

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    compile group: 'io.vavr', name: 'vavr', version: '0.9.1'
    compile group: 'io.vavr', name: 'vavr-jackson', version: '0.9.1'
}

With application configurured like this:

@SpringBootApplication
public class BootvavrApplication {

    public static void main(String[] args) {
        SpringApplication.run(BootvavrApplication.class, args);
    }

    @Bean
    Module vavrModule() {
        return new VavrModule();
    }
}

and controller mapped like this:

import io.vavr.collection.List;
@RestController
class TestController {

    @GetMapping("/test")
    List<String> testing() {
        return List.of("test", "test2");
    }
}

output is:

["test","test2"]

You can check the code out here: https://github.com/mihn/bootvavr

like image 53
mihn Avatar answered Nov 20 '22 02:11

mihn


I'm not familiar with SpringBoot, but it seems you should return the VavrModule object instead of the ObjectMapper in your jacksonBuilder() method.

I base myself on these links:

  • https://github.com/vavr-io/vavr/issues/1885#issuecomment-284037353

  • https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring#jackson-modules

like image 37
Ruslan Sennov Avatar answered Nov 20 '22 04:11

Ruslan Sennov


The ObjectMapper you register as a bean is just not the one being use to serialize into JSON, Spring MVC won't use it to as it's not what he is looking for.

To handle content for HTTP Spring MVC uses HttpMessageConverter and if he detects Jackson in the classpath a MappingJackson2HttpMessageConverter will be autoconfigured if not specified.

In Spring Boot adding should be enough to register the custom MessageConverter:

@Bean  
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();   
    ObjectMapper mapper = new ObjectMapper();

    mapper.registerModule(new VavrModule());

    // Spring MVC default Objectmapper configuration
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);

    jacksonConverter.setObjectMapper(mapper);   
    return jacksonConverter;  
}

In a pure Spring MVC application you would have to had the following code in the WebMvcConfigurerAdapter class:

@Bean  
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();   
    ObjectMapper mapper = new ObjectMapper();

    mapper.registerModule(new VavrModule());

    // Spring MVC default Objectmapper configuration
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);

    jacksonConverter.setObjectMapper(mapper);   
    return jacksonConverter;  
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(customJackson2HttpMessageConverter());
    super.addDefaultHttpMessageConverters();
}

Some Documentation about ObjectMapper in Spring MVC

Hope it helped!

like image 25
Baptiste Beauvais Avatar answered Nov 20 '22 04:11

Baptiste Beauvais