How to use alternative serializers instead of Jackson with Spring Boot

How to use alternative serializers instead of Jackson with Spring Boot

Spring Boot relies on the Jackson library as its default serializer and deserializer for objects. Jackson is included as a transitive dependency in the spring-boot-starter-web package. However, there may be cases where using a different serializer is preferred. This article guides how to use alternative serializers instead of Jackson with Spring Boot, allowing you to customize your serialization approach to better suit your needs.

Introduction

While Jackson is widely considered to be the most utilized serializer, there may be cases where it is necessary to switch to an alternative. If you decide to change, it is generally best to do early in the development process, before the codebase becomes too large. Attempting to change a serializer on an established project could lead to broken endpoints and other issues.

It is vital to consider that different serializers can behave differently, particularly when used in combination with other libraries. Therefore, it is recommended to have comprehensive contractual test coverage to ensure that everything works as expected.

Using a Spring Boot-supported serializer

If your preferred serializer, such as Gson, is supported by Spring Boot, you can replace Jackson with just a few configuration changes. For instance, to use Gson instead of Jackson, you can start by excluding Jackson from the spring-boot-starter-web dependency.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </exclusion>
    </exclusions>
</dependency>

After excluding Jackson from the spring-boot-starter-web, the next step is to add the Gson dependency to the project.

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>

To complete the process, you can open the application.properties file and configure it by setting the following property.

spring.http.converters.preferred-json-mapper=gson

Using an unsupported serializer with Spring Boot

When it comes to serializers that are not supported by Spring Boot, some additional configurations are required. For instance, if we take the example of Genson, even though it is not natively supported by Spring Boot, the library maintainers have added Spring components that enable easy integration of the serializer with Spring Boot.

To use Genson with Spring Boot, the first step is to remove Jackson from the project.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Next, you need to add the Genson dependency,

<dependency>
    <groupId>com.owlike</groupId>
    <artifactId>genson</artifactId>
    <version>1.6</version>
</dependency>

As a last step, create two Genson beans as follows,

package com.madadipouya.springcustomserializer.config;

import com.owlike.genson.Genson;
import com.owlike.genson.GensonBuilder;
import com.owlike.genson.ext.spring.GensonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GensonConfig {

    @Bean
    public Genson createGenson() {
        return new GensonBuilder()
                .setHtmlSafe(true)
                .setSkipNull(true)
                .useBeanViews(true)
                .create();
    }

    @Bean
    public GensonMessageConverter createGensonMessageConverter() {
        return new GensonMessageConverter(createGenson());
    }
}

Now, Spring should use Genson instead of Jackson.

However, if you want to use a serializer that doesn’t have Spring support, you’ll need to extend the AbstractJsonHttpMessageConverter class and override the readInternal and writeInternal methods for deserialization and serialization to work.

For example, if you want to use the Moshi serializer instead of Jackson, you’ll need to remove the Jackson dependency and add Moshi and Moshi Adapter to your project.

<dependency>
    <groupId>com.squareup.moshi</groupId>
    <artifactId>moshi</artifactId>
    <version>1.14.0</version>
</dependency>
<dependency>
    <groupId>com.squareup.moshi</groupId>
    <artifactId>moshi-adapters</artifactId>
    <version>1.14.0</version>
</dependency>

After that, you can extend the AbstractJsonHttpMessageConverter class and implement the required methods for Moshi to work with Spring Boot.

package com.madadipouya.springcustomserializer.config;

import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.AbstractJsonHttpMessageConverter;

import java.io.*;
import java.lang.reflect.Type;
import java.util.List;

public class MoshiHttpMessageConverter extends AbstractJsonHttpMessageConverter {

    private Moshi moshi;

    public MoshiHttpMessageConverter() {
        this.moshi = new Moshi.Builder().build();
        this.setSupportedMediaTypes(List.of(MediaType.APPLICATION_JSON));
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    protected Object readInternal(Type resolvedType, Reader reader) throws Exception {
        try (BufferedSource source = Okio.buffer(Okio.source(asInputStream(reader)))) {
            JsonAdapter<?> adapter = moshi.adapter(resolvedType);
            return adapter.fromJson(source);
        }
    }

    @Override
    protected void writeInternal(Object object, Type type, Writer writer) throws Exception {
        JsonAdapter<Object> adapter = moshi.adapter(type);
        BufferedSink bufferedSink = Okio.buffer(Okio.sink(asOutputStream(writer)));
        adapter.toJson(bufferedSink, object);
        bufferedSink.flush();
    }

    private InputStream asInputStream(Reader reader) {
        return new InputStream() {
            @Override
            public int read() throws IOException {
                return reader.read();
            }
        };
    }

    private OutputStream asOutputStream(Writer writer) {
        return new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                writer.write(b);
            }
        };
    }
}

As the final step, you should register MoshiHttpMessageConverter using WebMvcConfigurer.

package com.madadipouya.springcustomserializer.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new MoshiHttpMessageConverter());
    }
}

Conclusion

In this article, we covered how to use alternative serializers instead of Jackson with Spring Boot. We explored three alternative serializers Gson, Genson, and Moshi. For Gson, Spring Boot has native support for the serializer, and swapping it out only requires a minor configuration change. For Genson, the library maintainers have provided a Spring Boot module that simplifies the configuration process by instantiating Genson Spring beans. However, in the case of Moshi, we had to manually wire everything together as neither Spring Boot nor the library support the integration. That requires us to implement a custom Json message converter and register it with the application.

Examples of using Moshi and Genson with Spring Boot are available on GitHub at the links below:

Inline/featured images credits