I am using the Spring Framework, version 4.1.6, with Spring web services and without Spring Boot. To learn the framework, I am writing a REST API and am testing to make sure that the JSON response received from hitting an endpoint is correct. Specifically, I am trying to adjust the ObjectMapper
's PropertyNamingStrategy
to use the "lower case with underscores" naming strategy.
I am using the method detailed on Spring's blog to create a new ObjectMapper
and add it to the list of converters. This is as follows:
package com.myproject.config;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = jacksonBuilder();
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
return builder;
}
}
Then I run the following test (using JUnit, MockMvc, and Mockito) to verify my changes:
package com.myproject.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.AnnotationConfigWebContextLoader;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
// Along with other application imports...
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebConfig.class}, loader = AnnotationConfigWebContextLoader.class)
public class MyControllerTest {
@Mock
private MyManager myManager;
@InjectMocks
private MyController myController;
private MockMvc mockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(this.myController).build();
}
@Test
public void testMyControllerWithNameParam() throws Exception {
MyEntity expected = new MyEntity();
String name = "expected";
String title = "expected title";
// Set up MyEntity with data.
expected.setId(1); // Random ID.
expected.setEntityName(name);
expected.setEntityTitle(title)
// When the MyManager instance is asked for the MyEntity with name parameter,
// return expected.
when(this.myManager.read(name)).thenReturn(expected);
// Assert the proper results.
MvcResult result = mockMvc.perform(
get("/v1/endpoint")
.param("name", name))
.andExpect(status().isOk())
.andExpect((content().contentType("application/json;charset=UTF-8")))
.andExpect(jsonPath("$.entity_name", is(name))))
.andExpect(jsonPath("$.entity_title", is(title)))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
However, this returns a response of:
{"id": 1, "entityName": "expected", "entityTitle": "expected title"}
When I should get:
{"id": 1, "entity_name": "expected", "entity_title": "expected title"}
I have an implemented WebApplicationInitializer that scans for the package:
package com.myproject.config;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.scan("com.myproject.config");
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
servletContext.addListener(new ContextLoaderListener(ctx));
}
}
Using my debugger within IntelliJ, I can see that the builder is created and added, but somewhere down the line the resulting ObjectMapper
is not actually used. I must be missing something, but all the examples I've managed to find don't seem to mention what that is! I've tried eliminating @EnableWebMvc
and implementing WebMvcConfigurationSupport
, using MappingJackson2HttpMessageConverter
as a Bean, and setting ObjectMapper
as a Bean to no avail.
Any help would be greatly appreciated! Please let me know if any other files are required.
Thanks!
EDIT: Was doing some more digging and found this. In the link, the author appends setMessageConverters()
before he/she builds MockMvc and it works for the author. Doing the same worked for me as well; however, I'm not sure if everything will work in production as the repositories aren't flushed out yet. When I find out I will submit an answer.
EDIT 2: See answer.
See Question&Answers more detail:
os