Force SpingBoot to use Gson over Jackson

Viewed 1387

I am trying to force SpringBoot to use Gson instead of Jackson. I've read most of the articles I've found online and I am still seeing Jackson being used. Here's what I've done

  1. Added
spring:
  http: { converters: { preferred-json-mapper: gson } }
  mvc: { converters: {preferred-json-mapper: gson } }

in application.yaml

  1. Updated POM
    • Added gson dependency
    • Added jackson-databind to exclusion list in spring-boot-starter-web depedency.
  2. Added @EnableAutoConfiguration(exclude = JacksonAutoConfiguration.class) to main class.
  3. Written below @Configuration class:
@Configuration
@Slf4j
public class MyConfig implements WebMvcConfigurer {
  
  @Override
  public void extendMessageConverters (List<HttpMessageConverters<?>> converters) {
    log.debug("Setting gson converter");
    converters.add(new GsonHttpMessageConverter(myCustomGsonInstance()));
  }

  public Gson myCustomGsonInstance() {
    return new Gson();
  }
}

When running tests in debug, I can see that Jackson is still listed in the HttpMessageConverters list and Gson is not.


Update: This behavior is seen while running live and in the below test class.

@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = MOCK)
@ExtendWith(MockitoExtension.class)
public class MyTestClass {

  @Autowired
  private MyController controller;
  
  private MockMvc mockMvc;

  @BeforeEach
  public void setUp(){
    mockMvc = MockMvcBuilders.standaloneSetup(controller)
    // .setMessageConverters(new GsonHttpMessageConverter(myCustomGsonInstance())) // if I add this, the test passes.
    .build();
  }
  
  @Test
  public void happyFlow(){
    // given
    URI uri = "/test/uri";
    HttpHeaders headers = new HttpHeaders();
    headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
    
    // when
    String responseBody = mockMvc.perform(get(uri).headers(headers)).andReturn().getResponse().getContentAsString();

    // then
    assertThat(responseBody, wasSerializedByGson());
  }
}
2 Answers

It looks like you're using the wrong property for configuring the preferred JSON mapper. You are using spring.http.converters.preferred-json-mapper but the correct property is spring.mvc.converters.preferred-json-mapper. In application.yaml, that would be the following:

spring:
  mvc:
    converters:
      preferred-json-mapper: gson

Spring Boot comes with Gson Auto Configuration support: Source Code

So you have to Autowire the Gson singleton instance to be used by your WebMvcConfigurer in addition to enabling the yaml property:

@Configuration
@Slf4j
public class MyConfig implements WebMvcConfigurer {

  @Autowired
  private Gson gson;
  
  @Override
  public void extendMessageConverters (List<HttpMessageConverters<?>> converters) {
    log.debug("Setting gson converter");
    converters.add(new GsonHttpMessageConverter(gson));
  }

}

And the yaml properties borrowed from Andy Wilkinson:

spring:
  mvc:
    converters:
      preferred-json-mapper: gson

With this setup Spring MVC is using the same Gson instance as the one Autowired in your configuration.

And in your test, it should look like this:

@WebMvcTest(MyController.class)
public class MyTestClass {

  @Autowired
  private MockMvc mockMvc;
  @Autowired
  private MyController controller;

  @Test
  public void happyFlow(){
    // given
    URI uri = "/test/uri";
    HttpHeaders headers = new HttpHeaders();
    headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
    
    // when
    String responseBody = mockMvc.perform(get(uri).headers(headers)).andReturn().getResponse().getContentAsString();

    // then
    assertThat(responseBody, wasSerializedByGson());
  }
}
Related