In this tutorial, we will learn how to create and register a custom HttpMessageConverter. The converter will convert the request csv body data to user defined object. The converter will also convert the response user object to csv data. We are going to use OpenCSV parser library for Java object to CSV (and vice-versa) conversion.
Converting request body CSV to Java object list
Creating the Controller
@Controller
public class ExampleController {
@RequestMapping(
value = "/newEmployee",
consumes = "text/csv",
produces = MediaType.TEXT_PLAIN_VALUE,
method = RequestMethod.POST)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public String handleRequest (@RequestBody EmployeeList employeeList) {
System.out.printf("In handleRequest method, employeeList: %s%n", employeeList.getList());
String s = String.format("size: " + employeeList.getList().size());
System.out.println(s);
return s;
}
.............
}
public class EmployeeList extends ListParam<Employee> {
}
public class ListParam<T> {
private List<T> list;
public List<T> getList () {
return list;
}
public void setList (List<T> list) {
this.list = list;
}
}
package com.logicbig.example;
import com.opencsv.bean.CsvBindByName;
public class Employee {
@CsvBindByName
private String id;
@CsvBindByName
private String name;
@CsvBindByName
private String phoneNumber;
.............
}
Creating the Converter
package com.logicbig.example;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
public class CsvHttpMessageConverter<T, L extends ListParam<T>>
extends AbstractHttpMessageConverter<L> {
public CsvHttpMessageConverter () {
super(new MediaType("text", "csv"));
}
@Override
protected boolean supports (Class<?> clazz) {
return ListParam.class.isAssignableFrom(clazz);
}
@Override
protected L readInternal (Class<? extends L> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
Class<T> t = toBeanType(clazz.getGenericSuperclass());
strategy.setType(t);
CSVReader csv = new CSVReader(new InputStreamReader(inputMessage.getBody()));
CsvToBean<T> csvToBean = new CsvToBean<>();
List<T> beanList = csvToBean.parse(strategy, csv);
try {
L l = clazz.newInstance();
l.setList(beanList);
return l;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
protected void writeInternal (L l, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
strategy.setType(toBeanType(l.getClass().getGenericSuperclass()));
OutputStreamWriter outputStream = new OutputStreamWriter(outputMessage.getBody());
StatefulBeanToCsv<T> beanToCsv =
new StatefulBeanToCsvBuilder(outputStream)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.withMappingStrategy(strategy)
.build();
try {
beanToCsv.write(l.getList());
outputStream.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
private Class<T> toBeanType (Type type) {
return (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
}
}
Registering the Converter
@EnableWebMvc
@ComponentScan("com.logicbig.example")
public class AppConfig extends WebMvcConfigurerAdapter {
@Override
public void extendMessageConverters (List<HttpMessageConverter<?>> converters) {
converters.add(new CsvHttpMessageConverter<>());
}
}
Writing JUnit test
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = AppConfig.class)
public class ControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
.............
@Test
public void testConsumerController () throws Exception {
MockHttpServletRequestBuilder builder =
MockMvcRequestBuilders.post("/newEmployee")
.contentType("text/csv")
.accept(MediaType.TEXT_PLAIN_VALUE)
.content(getNewEmployeeListInCsv());
this.mockMvc.perform(builder)
.andExpect(MockMvcResultMatchers.status()
.isOk())
.andExpect(MockMvcResultMatchers.content()
.string("size: 3"))
.andDo(MockMvcResultHandlers.print());
;
}
.............
public String getNewEmployeeListInCsv () {
return "id, name, phoneNumber\n1,Joe,123-212-3233\n2,Sara,132,232,3111\n" +
"3,Mike,111-222-3333\n";
}
} Output In handleRequest method, employeeList: [Employee{id='1', name='Joe', phoneNumber='123-212-3233'}, Employee{id='2', name='Sara', phoneNumber='132'}, Employee{id='3', name='Mike', phoneNumber='111-222-3333'}] size: 3
MockHttpServletRequest: HTTP Method = POST Request URI = /newEmployee Parameters = {} Headers = {Content-Type=[text/csv], Accept=[text/plain]}
Handler: Type = com.logicbig.example.ExampleController Method = public java.lang.String com.logicbig.example.ExampleController.handleRequest(com.logicbig.example.EmployeeList)
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=[7]} Content type = text/plain;charset=ISO-8859-1 Body = size: 3 Forwarded URL = null Redirected URL = null Cookies = []
Returning HTTP response in CSV
@Controller
public class ExampleController {
.............
@RequestMapping(
value = "/employeeList",
produces = "text/csv",
method = RequestMethod.GET)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public EmployeeList handleRequest2 () {
List<Employee> list = Arrays.asList(
new Employee("1", "Tina", "111-111-1111"),
new Employee("2", "John", "222-222-2222")
);
EmployeeList employeeList = new EmployeeList();
employeeList.setList(list);
return employeeList;
}
}
@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("/employeeList")
.accept("text/csv");
this.mockMvc.perform(builder)
.andExpect(MockMvcResultMatchers.status()
.isOk())
.andDo(MockMvcResultHandlers.print());
}
.............
} Output
MockHttpServletRequest: HTTP Method = GET Request URI = /employeeList Parameters = {} Headers = {Accept=[text/csv]}
Handler: Type = com.logicbig.example.ExampleController Method = public com.logicbig.example.EmployeeList com.logicbig.example.ExampleController.handleRequest2()
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/csv]} Content type = text/csv Body = ID,NAME,PHONENUMBER 1,Tina,111-111-1111 2,John,222-222-2222
Forwarded URL = null Redirected URL = null Cookies = []
Example ProjectDependencies 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.
- opencsv 3.9: A simple library for reading and writing CSV in Java.
- JDK 1.8
- Maven 3.3.9
|