Close

Spring MVC - HttpMessageConverter for YAML conversion

[Last Updated: Sep 2, 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";
    }
}

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());
    }
    .............
}


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
                • YamlHttpMessageConverter.java
        • test
          • java
            • com
              • logicbig
                • example

    See Also