Spring MVC - HttpMessageConverter for YAML conversion

[Updated: Sep 2, 2017, Created: May 27, 2017]

In the last tutorial, we got familiar with creating a custom HTTPMessageConverter. In this tutorial, we will create another one. This HTTPMessageConverter will convert request body containing YAML content to user defined object and vice-versa. We are going to use SnakeYAML, which is a Java based processor for parsing YAML data to/from Java Objects.

Creating the Controller

@Controller
public class ExampleController {
  
  @RequestMapping(
            value = "/newEmployee",
            consumes = "text/yaml",
            produces = MediaType.TEXT_PLAIN_VALUE,
            method = RequestMethod.POST)
  @ResponseBody
  @ResponseStatus(HttpStatus.OK)
  public String handleRequest (@RequestBody Employee employee) {
      System.out.printf("In handleRequest method, employee: %s%n", employee);
      String s = String.format("Employee saved: " + employee.getName());
      System.out.println(s);
      return s;
  }
  
  @RequestMapping(
            value = "/employee",
            produces = "text/yaml",
            method = RequestMethod.GET)
  @ResponseBody
  @ResponseStatus(HttpStatus.OK)
  public Employee handleRequest2 (@RequestParam String id) {
      //create a test employee
      return new Employee(id, "Tina", "111-111-1111");
  }
}
public class Employee {
  private String id;
  private String name;
  private String phoneNumber;
    .............
}

Creating the Converter

public class YamlHttpMessageConverter<T>
        extends AbstractHttpMessageConverter<T> {
  
  public YamlHttpMessageConverter () {
      super(new MediaType("text", "yaml"));
  }
  
  @Override
  protected boolean supports (Class<?> clazz) {
      return true;
  }
  
  @Override
  protected T readInternal (Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
      Yaml yaml = new Yaml();
      T t = yaml.loadAs(inputMessage.getBody(), clazz);
      return t;
  }
  
  @Override
  protected void writeInternal (T t, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
      Yaml yaml = new Yaml();
      OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody());
      yaml.dump(t, writer);
      writer.close();
  }
}

Registering the Converter

@EnableWebMvc
@ComponentScan("com.logicbig.example")
public class AppConfig extends WebMvcConfigurerAdapter {
  
  @Override
  public void extendMessageConverters (List<HttpMessageConverter<?>> converters) {
      converters.add(new YamlHttpMessageConverter<>());
  }
}

Testing with JUnit tests

Testing request

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = AppConfig.class)
public class ControllerTest {
  @Autowired
  private WebApplicationContext wac;
  private MockMvc mockMvc;

  @Before
  public void setup () {
      DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
      this.mockMvc = builder.build();
  }
    .............
  @Test
  public void testConsumerController () throws Exception {
      MockHttpServletRequestBuilder builder =
                MockMvcRequestBuilders.post("/newEmployee")
                                      .contentType("text/yaml")
                                      .accept(MediaType.TEXT_PLAIN_VALUE)
                                      .content(getNewEmployeeInYaml());
      this.mockMvc.perform(builder)
                  .andExpect(MockMvcResultMatchers.status()
                                                  .isOk())
                  .andExpect(MockMvcResultMatchers.content()
                                                  .string("Employee saved: Tina"))
                  .andDo(MockMvcResultHandlers.print());
      ;
  }
    .............
  public String getNewEmployeeInYaml () {
      return "id: 1\nname: Tina\nphoneNumber: 111-111-1111\n";
  }
}

Output


In handleRequest method, employee: Employee{id='1', name='Tina', phoneNumber='111-111-1111'}
Employee saved: Tina

MockHttpServletRequest:
HTTP Method = POST
Request URI = /newEmployee
Parameters = {}
Headers = {Content-Type=[text/yaml], Accept=[text/plain]}

Handler:
Type = com.logicbig.example.ExampleController
Method = public java.lang.String com.logicbig.example.ExampleController.handleRequest(com.logicbig.example.Employee)

Async:
Async started = false
Async result = null

Resolved Exception:
Type = null

ModelAndView:
View name = null
View = null
Model = null

FlashMap:
Attributes = null

MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[20]}
Content type = text/plain;charset=ISO-8859-1
Body = Employee saved: Tina
Forwarded URL = null
Redirected URL = null
Cookies = []

Testing response

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = AppConfig.class)
public class ControllerTest {
  @Autowired
  private WebApplicationContext wac;
  private MockMvc mockMvc;
    .............
  @Test
  public void testProducerController () throws Exception {
      MockHttpServletRequestBuilder builder =
                MockMvcRequestBuilders.get("/employee")
                                      .accept("text/yaml")
                                      .param("id", "1");
      this.mockMvc.perform(builder)
                  .andExpect(MockMvcResultMatchers.status()
                                                  .isOk())
                  .andDo(MockMvcResultHandlers.print());
  }
    .............
}

Output



MockHttpServletRequest:
HTTP Method = GET
Request URI = /employee
Parameters = {id=[1]}
Headers = {Accept=[text/yaml]}

Handler:
Type = com.logicbig.example.ExampleController
Method = public com.logicbig.example.Employee com.logicbig.example.ExampleController.handleRequest2(java.lang.String)

Async:
Async started = false
Async result = null

Resolved Exception:
Type = null

ModelAndView:
View name = null
View = null
Model = null

FlashMap:
Attributes = null

MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/yaml]}
Content type = text/yaml
Body = !!com.logicbig.example.Employee {id: '1', name: Tina, phoneNumber: 111-111-1111}

Forwarded URL = null
Redirected URL = null
Cookies = []

Example Project

Dependencies and Technologies Used :

  • spring-webmvc 4.3.8.RELEASE: Spring Web MVC.
  • spring-test 4.3.8.RELEASE: Spring TestContext Framework.
  • javax.servlet-api 3.0.1 Java Servlet API
  • junit 4.12: JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
  • snakeyaml 1.18: YAML 1.1 parser and emitter for Java.
  • JDK 1.8
  • Maven 3.3.9

Yaml Message Converter Select All Download
  • custom-message-converter-yaml
    • src
      • main
        • java
          • com
            • logicbig
              • example
      • test
        • java
          • com
            • logicbig
              • example

See Also