In an earlier article, I discussed how to read and write CSV files using Apache Commons CSV. This article is a direct continuation of the previous article and part of the series to learn about libraries for reading and writing CSV files in Java.

Today, we will learn about another open-source library — OpenCSV to read and write CSV files in Java. OpenCSV is a very popular library for reading, writing, parsing, serializing, and deserializing CSV files in Java.

Dependencies

Before we move on to reading or writing CSV files, you only need OpenCSV dependency to your project. If you are using Gradle, add the following dependency to your build.gralde file:

implementation 'com.opencsv:opencsv:4.6'

For Maven project, you should add the below dependency to the pom.xml file:

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

Sample CSV Files

Let us use the same CSV files we used for the Commons CSV examples for reading and parsing using OpenCSV.

Here is the first CSV without a header:

users.csv

1,Atta Shah,atta@example.com,PK
2,Alex Jones,alex@example.com,DE
3,Jovan Lee,jovan@example.com,FR
4,Greg Hover,greg@example.com,US

The second CSV file with a header:

users-with-header.csv

id,name,email,country
1,Atta Shah,atta@example.com,PK
2,Alex Jones,alex@example.com,DE
3,Jovan Lee,jovan@example.com,FR
4,Greg Hover,greg@example.com,US

Reading CSV Files

There are multiple ways to read a CSV file with OpenCSV. You can choose to read the CSV file either line-by-line or at once. Similarly, you can decide whether to read the record as a string array or bind the record into an object. Let us look at all these approaches below.

Reading a CSV file with each record as a string array

The simplest way to read a CSV file using OpenCSV is reading each record one by one into a string array. Here is an example that uses the CSVReader class to read one line at a time from the file:

try {
    // create a reader
    Reader reader = Files.newBufferedReader(Paths.get("users.csv"));

    // create csv reader
    CSVReader csvReader = new CSVReader(reader);

    // read one record at a time
    String[] record;
    while ((record = csvReader.readNext()) != null) {
        System.out.println("ID: " + record[0]);
        System.out.println("Name: " + record[1]);
        System.out.println("Email: " + record[2]);
        System.out.println("Country: " + record[3]);
    }

    // close readers
    csvReader.close();
    reader.close();

} catch (IOException ex) {
    ex.printStackTrace();
}

Reading all records at once

In the example above, the readNext() method reads the next line from the buffer and converts to a string array. The CSVReader class also provides a method called readAll() that reads the entire file into a List with each element being a String[] of tokens:

try {
    // create a reader
    Reader reader = Files.newBufferedReader(Paths.get("users.csv"));

    // create csv reader
    CSVReader csvReader = new CSVReader(reader);

    // read all records at once
    List<String[]> records = csvReader.readAll();

    // iterate through list of records
    for (String[] record : records) {
        System.out.println("ID: " + record[0]);
        System.out.println("Name: " + record[1]);
        System.out.println("Email: " + record[2]);
        System.out.println("Country: " + record[3]);
    }

    // close readers
    csvReader.close();
    reader.close();

} catch (IOException ex) {
    ex.printStackTrace();
}

The above approach is not recommended for larger CSV files as it loads the entire file contents into memory.

Skipping the header

There is no way to skip the header record when using the CSVReader class. If you read a file that contains a header, then the header will also be printed on the console.

Instead, you should use CSVReaderBuilder that provides greater flexibility and more configuration options including the ability to skip the header record.

Let us use the CSVReaderBuilder class to create a CSVReader object with specified number of records skipped:

CSVReader csvReader = new CSVReaderBuilder(reader).withSkipLines(1).build();

CSVParserBuilder allows you to choose a custom column separator, ignore or handle quotations marks, decide what to do with null fields, and how to interpret escaped characters:

CSVParser parser = new CSVParserBuilder()
        .withSeparator('\t')
        .withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_QUOTES)
        .withIgnoreLeadingWhiteSpace(true)
        .withIgnoreQuotations(false)
        .withStrictQuotes(true)
        .build();

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

Check out the official documentation for more information on these configuration options.

Reading a CSV file with each record as a Java object using annotations

The real benefit of using OpenCSV is that you can directly map the record fields into a Java object. There are two ways of doing this. You can either use annotations or mapping strategies to bind the record fields to bean attributes.

OpenCSV has two types of annotations to specify the column names mapping with object fields either by name or by position: @CsvBindByName and @CsvBindByPosition.

Using @CsvBindByName annotation

You can only use the @CsvBindByName annotation if the CSV file has a header. It accepts up to five parameters like column, required, and locale. All parameters are options except column which is also only required if the header column name in the CSV file is different from the bean field.

Let us first create a Java class to make use of CsvBindByName annotation:

User.java

public class User {

    @CsvBindByName
    public int id;
    @CsvBindByName
    public String name;
    @CsvBindByName
    public String email;
    @CsvBindByName(column = "country")
    public String countryCode;

    // getters and setters omitted for brevity
}

Here is an example that reads and parses the CSV file records directly into Java objects using OpenCSV:

try {
    // create a reader
    Reader reader = Files.newBufferedReader(Paths.get("users-with-header.csv"));

    // create csv bean reader
    CsvToBean csvToBean = new CsvToBeanBuilder(reader)
            .withType(User.class)
            .withIgnoreLeadingWhiteSpace(true)
            .build();

    // iterate through users
    for (User user : (Iterable<User>) csvToBean) {
        System.out.println("ID: " + user.getId());
        System.out.println("Name: " + user.getName());
        System.out.println("Email: " + user.getEmail());
        System.out.println("Country: " + user.getCountryCode());
    }

    // close the reader
    reader.close();

} catch (IOException ex) {
    ex.printStackTrace();
}

Note that we used users-with-headers.csv file for the above example because it contains a header.

The CsvToBean class also provides a parse() method that reads entire CSV file contents into memory and then parses it into a list of objects (not recommended for large CSV files):

List<User> users = csvToBean.parse();

// iterate through list
for (User user : users) {
    System.out.println("ID: " + user.getId());
    System.out.println("Name: " + user.getName());
    System.out.println("Email: " + user.getEmail());
    System.out.println("Country: " + user.getCountryCode());
}

Using @CsvBindByPosition annotation

If the CSV file does not have a header, you use the @CsvBindByPosition annotation to map the column position (zero-based) to bean fields like below:

public class User {

    @CsvBindByPosition(position = 0)
    public int id;
    @CsvBindByPosition(position = 1)
    public String name;
    @CsvBindByPosition(position = 2)
    public String email;
    @CsvBindByPosition(position = 3)
    public String countryCode;

    // getters and setters omitted for brevity
}

Reading a CSV file with each record as a Java object using mapping strategies

Mapping strategies are another way of mapping the CSV columns directly to Java object fields. Using this, you can safely remove all OpenCSV annotations from your Java classes.

Let us first remove all annotations from the User class:

public class User {

    public int id;
    public String name;
    public String email;
    public String countryCode;

    public User(int id, String name, String email, String countryCode) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.countryCode = countryCode;
    }

    // getters and setters omitted for brevity
}

Now let us use ColumnPositionMappingStrategy to specify the mapping between CSV columns and Java object attributes, and then parse the CSV records into Java objects:

try {
    // create a reader
    Reader reader = Files.newBufferedReader(Paths.get("users-with-header.csv"));

    // columns name
    String[] columns = {"id", "name", "email", "countryCode"};

    // create a mapping strategy
    ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();
    strategy.setType(User.class);
    strategy.setColumnMapping(columns);

    // create csv bean reader
    CsvToBean csvToBean = new CsvToBeanBuilder(reader)
            .withMappingStrategy(strategy)
            .withSkipLines(1)
            .withIgnoreLeadingWhiteSpace(true)
            .build();

    // iterate through users
    for (User user : (Iterable<User>) csvToBean) {
        System.out.println("ID: " + user.getId());
        System.out.println("Name: " + user.getName());
        System.out.println("Email: " + user.getEmail());
        System.out.println("Country: " + user.getCountryCode());
    }

    // close the reader
    reader.close();

} catch (IOException ex) {
    ex.printStackTrace();
}

The ColumnPositionMappingStrategy class uses the position of the column in CSV file to map it to bean attribute.

Writing CSV Files

OpenCSV allows you to generate a CSV file either from an array of strings or from a list of objects. It has more configuration options than Commons CSV for writing data to CSV files. Most importantly, you can easily convert any list of objects to a CSV file by writing just a few lines of code.

Generating a CSV file from an array of strings

Here is an example that writes an array of strings to a CSV file by using OpenCSV:

try {
    // create a write
    Writer writer = Files.newBufferedWriter(Paths.get("users-simple.csv"));

    // header record
    String[] headerRecord = {"id", "name", "email", "country"};

    // create a csv writer
    ICSVWriter csvWriter = new CSVWriterBuilder(writer)
            .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
            .withQuoteChar(CSVWriter.NO_QUOTE_CHARACTER)
            .withEscapeChar(CSVWriter.DEFAULT_ESCAPE_CHARACTER)
            .withLineEnd(CSVWriter.DEFAULT_LINE_END)
            .build();

    // write header record
    csvWriter.writeNext(headerRecord);

    // write data records
    csvWriter.writeNext(new String[] {"1", "Emma Watson", "emma.watson@example.com", "UK"});
    csvWriter.writeNext(new String[] {"2", "Nick Jones", "nick.jones@example.com", "DE"});
    csvWriter.writeNext(new String[] {"3", "Shanzay Alai", "shanzay.alai@example.com", "US"});

    // close writers
    csvWriter.close();
    writer.close();

} catch (IOException ex) {
    ex.printStackTrace();
}

Generating a CSV file from a list of objects

Finally, here is an example that shows how to convert a list of objects into a CSV file. It is using the User class we defined in the previous example:

try {
    // create a write
    Writer writer = Files.newBufferedWriter(Paths.get("users-objects.csv"));

    // create a csv writer
    StatefulBeanToCsv<User> csvWriter = new StatefulBeanToCsvBuilder<User>(writer)
            .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
            .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
            .withEscapechar(CSVWriter.DEFAULT_ESCAPE_CHARACTER)
            .withLineEnd(CSVWriter.DEFAULT_LINE_END)
            .withOrderedResults(false)
            .build();

    // create a list of objects (`User`)
    List<User> users = new ArrayList<>();
    users.add(new User(1, "Emma Watson", "emma.watson@example.com", "UK"));
    users.add(new User(2, "Nick Jones", "nick.jones@example.com", "DE"));
    users.add(new User(3, "Shanzay Alai", "shanzay.alai@example.com", "US"));

    // write list of objects
    csvWriter.write(users);

    // close the writer
    writer.close();

} catch (Exception ex) {
    ex.printStackTrace();
}

Conclusion

That is all folks for reading and writing CSV files in Java by using OpenCSV library. We discussed almost all ways to write and read data from a CSV file. OpenCSV is a simplest yet very powerful CSV parser that makes it a popular choice when it comes to handling CSV files in Java.

I appreciate your patience for reading this long article.

Further Reading

I hope you enjoy reading this article. You may be interested in reading other CSV related articles:

✌️ Like this article? Follow @attacomsian on Twitter. You can also follow me on LinkedIn and DEV. Buy me a coffee (cost $3)

Need help to start a new Spring Boot or MEAN stack project? I am available for contract work. Hire me to accomplish your business goals with engineering and design. Let’s talk about your project: hi@attacomsian.com.