1. Introduction

This quick article introduces OpenCSV 4, a fantastic library for writing, reading, serializing, deserializing, and/or parsing .csv files! Below, we'll go through several examples demonstrating how to set up and use OpenCSV 4 for your endeavors.

2. Set-Up

Here's how to add OpenCSV to your project by way of a pom.xml dependency:

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>4.1</version>
</dependency>

The .jars for OpenCSV can be found at the official site or through a quick search over at Maven Repository.

Our .csv file will be really simple, we'll keep it to two columns and four rows:

colA, ColB
A, B
C, D
G, G
G, F

3. To Bean or Not to Bean

After adding OpenCSV to your pom.xml, we can implement CSV-handling methods in two convenient ways:

  1. using the handy CSVReader and CSVWriter objects (for simpler operations) or
  2. using CsvToBean to convert .csv files into beans (which are implemented as annotated plain-old-java-objects).

We'll stick with synchronous (or blocking) examples for this article so we can focus on the basics.

Remember, a synchronous method will prevent surrounding or subsequent code from executing until it's done. Any production environment will likely use asynchronous or (non-blocking) methods that will allow other processes or methods to complete while the asynchronous method finishes up.

We'll dive into asynchronous examples for OpenCSV in a future article.

3.1. The CSVReader

CSVReader – through the supplied readAll() and readNext() methods! Let's take a look at how to use readAll() synchronously:

public List<String[]> readAll(Reader reader) throws Exception {
    CSVReader csvReader = new CSVReader(reader);
    List<String[]> list = new ArrayList<>();
    list = csvReader.readAll();
    reader.close();
    csvReader.close();
    return list;
}

Then we can call that method by passing in a BufferedReader:

public String readAllExample() throws Exception {
    Reader reader = Files.newBufferedReader(Paths.get(
      ClassLoader.getSystemResource("csv/twoColumn.csv").toURI()));
    return CsvReaderExamples.readAll(reader).toString();
}

Similarly, we can abstract readNext() which reads a supplied .csv line by line:

public List<String[]> oneByOne(Reader reader) throws Exception {
    List<String[]> list = new ArrayList<>();
    CSVReader csvReader = new CSVReader(reader);
    String[] line;
    while ((line = csvReader.readNext()) != null) {
        list.add(line);
    }
    reader.close();
    csvReader.close();
    return list;
}

And we can call that method here by passing in a BufferReader:

public String oneByOneExample() throws Exception {
    Reader reader = Files.newBufferedReader(Paths.get(
      ClassLoader.getSystemResource("csv/twoColumn.csv").toURI()));
    return CsvReaderExamples.oneByOne(reader).toString();
}

For greater flexibility and configuration options you can alternatively use CSVReaderBuilder:

CSVParser parser = new CSVParserBuilder()
    .withSeparator(',')
    .withIgnoreQuotations(true)
    .build();

CSVReader csvReader = new CSVReaderBuilder(reader)
    .withSkipLines(0)
    .withCSVParser(parser)
    .build();

CSVReaderBuilder allows one to skip the column headings and set parsing rules through CSVParserBuilder.

Using CSVParserBuilder, we can choose a custom column separator, ignore or handle quotations marks, state how we'll handle null fields, and how to interpret escaped characters. For more information on these configuration settings please refer to the official specification docs.

As always, please remember to close all your Readers to prevent memory leaks!

3.2. The CSVWriter

CSVWriter similarly supplies the ability to write to a .csv file all at once or line by line.

Let's take a look at how to do write to a .csv line by line:

public String csvWriterOneByOne(List<String[]> stringArray, Path path) throws Exception {
    CSVWriter writer = new CSVWriter(new FileWriter(path.toString()));
    for (String[] array : stringArray) {
        writer.writeNext(array);
    }
    
    writer.close();
    return Helpers.readFile(path);
}

Now, let's specify where we want to save that file and call the method we just wrote:

public String csvWriterOneByOne() throws Exception{
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/writtenOneByOne.csv").toURI()); 
    return CsvWriterExamples.csvWriterOneByOne(Helpers.fourColumnCsvString(), path); 
}

We can also write our .csv all at once by passing in a List of String arrays representing the rows of our .csv. :

public String csvWriterAll(List<String[]> stringArray, Path path) throws Exception {
     CSVWriter writer = new CSVWriter(new FileWriter(path.toString()));
     writer.writeAll(stringArray);
     writer.close();
     return Helpers.readFile(path);
}

And here's how we call it:

public String csvWriterAll() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/writtenAll.csv").toURI()); 
    return CsvWriterExamples.csvWriterAll(Helpers.fourColumnCsvString(), path);
}

That's it!

3.3. Bean-Based Reading

OpenCSV is able to serialize .csv files into preset and reusable schemas implemented as annotated Java pojo beans. CsvToBean is constructed using CsvToBeanBuilder. As of OpenCSV 4, CsvToBeanBuilder is the recommended way to work with com.opencsv.bean.CsvToBean.

Here's a simple bean we can use to serialize our two-column .csv from section 2.:

public class SimplePositionBean  {
    @CsvBindByPosition(position = 0)
    private String exampleColOne;

    @CsvBindByPosition(position = 1)
    private String exampleColTwo;

    // getters and setters
}

Each column in the .csv file is associated with a field in the bean. The mappings between .csv column headings can be performed using the @CsvBindByPosition or the @CsvBindByName annotations which specify a mapping by position or heading string match, respectively.

First, let's create a superclass called CsvBean – this will allow us to reuse and generalize the methods we'll build below:

public class CsvBean { }

An example child class:

public class NamedColumnBean extends CsvBean {

    @CsvBindByName(column = "name")
    private String name;

    @CsvBindByName
    private int age;

    // getters and setters
}

Let's abstract a synchronously returned List using the CsvToBean:

 public List<CsvBean> beanBuilderExample(Path path, Class clazz) throws Exception {
     CsvTransfer csvTransfer = new CsvTransfer();
     ColumnPositionMappingStrategy ms = new ColumnPositionMappingStrategy();
     ms.setType(clazz);

     Reader reader = Files.newBufferedReader(path);
     CsvToBean cb = new CsvToBeanBuilder(reader)
       .withType(clazz)
       .withMappingStrategy(ms)
       .build();

    csvTransfer.setCsvList(cb.parse());
    reader.close();
    return csvTransfer.getCsvList();
}

We pass in our bean (clazz) and set that as the ColumnPositionMappingStrategy. In doing so, we associate the fields of our beans with the respective columns of our .csv rows.

We can call that here using the SimplePositionBean subclass of the CsvBean we wrote above:

public String simplePositionBeanExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/twoColumn.csv").toURI()); 
    return BeanExamples.beanBuilderExample(path, SimplePositionBean.class).toString(); 
}

or here using the NamedColumnBean – another subclass of the CsvBean:

public String namedColumnBeanExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/namedColumn.csv").toURI()); 
    return BeanExamples.beanBuilderExample(path, NamedColumnBean.class).toString();
}

3.4. Bean-Based Writing

Lastly, let's take a look at how to use the StatefulBeanToCsv class to write to a .csv file:

public String writeCsvFromBean(Path path) throws Exception {
    Writer writer  = new FileWriter(path.toString());

    StatefulBeanToCsv sbc = new StatefulBeanToCsvBuilder(writer)
       .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
       .build();

    List<CsvBean> list = new ArrayList<>();
    list.add(new WriteExampleBean("Test1", "sfdsf", "fdfd"));
    list.add(new WriteExampleBean("Test2", "ipso", "facto"));

    sbc.write(list);
    writer.close();
    return Helpers.readFile(path);
}

Here, we are specifying how we will delimit our data which is supplied as a List of specified CsvBean objects.

We can then call our method writeCsvFromBean() after passing in the desired output file path:

public String writeCsvFromBeanExample() {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/writtenBean.csv").toURI()); 
    return BeanExamples.writeCsvFromBean(path); 
}

4. Conclusion

There we go – synchronous code examples for OpenCSV using beans, CSVReader, and CSVWriter. Check out the official docs here.

As always, the code samples are provided over on GitHub.