Read / Write CSV files in Java using OpenCSV

In an earlier article, I wrote about how to read and write CSV files in Java using Apache Commons CSV.

In this article, I’ll take you through another open source library called OpenCSV for reading and writing CSV files in Java.

Adding OpenCSV dependency

First of all, you need to add the OpenCSV dependency in your project. If you’re a Maven user, add the following dependency to your pom.xml file.

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

And here is the dependency for Gradle users -

compile "com.opencsv:opencsv:4.0"

Sample CSV file

Following are two sample CSV files that we’ll read and parse in the examples presented in this article.

CSV file without a header - users.csv

Rajeev Kumar Singh ♥,rajeevs@example.com,+91-9999999999,India
Sachin Tendulkar,sachin@example.com,+91-9999999998,India
Barak Obama,barak.obama@example.com,+1-1111111111,United States
Donald Trump,donald.trump@example.com,+1-2222222222,United States

CSV file with a header - users-with-header.csv

name,email,phone,country
Rajeev Kumar Singh ♥,rajeevs@example.com,+91-9999999999,India
Sachin Tendulkar,sachin@example.com,+91-9999999998,India
Barak Obama,barak.obama@example.com,+1-1111111111,United States
Donald Trump,donald.trump@example.com,+1-2222222222,United States

Read a CSV file (Retrieve each record as a String array)

The example below shows how to read and parse a CSV file using OpenCSV library. It reads the CSV records one by one into a String array -

import com.opencsv.CSVReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;

public class OpenCSVReader {
    private static final String SAMPLE_CSV_FILE_PATH = "./users.csv";

    public static void main(String[] args) throws IOException {
        try (
            Reader reader = Files.newBufferedReader(Paths.get(SAMPLE_CSV_FILE_PATH));
            CSVReader csvReader = new CSVReader(reader);
        ) {
            // Reading Records One by One in a String array
            String[] nextRecord;
            while ((nextRecord = csvReader.readNext()) != null) {
                System.out.println("Name : " + nextRecord[0]);
                System.out.println("Email : " + nextRecord[1]);
                System.out.println("Phone : " + nextRecord[2]);
                System.out.println("Country : " + nextRecord[3]);
                System.out.println("==========================");
            }
        }
    }
}

Reading all records at once

In the above example, We read the CSV records one by one using the readNext() method. CSVReader also provides a method called readAll() to read all the records at once into a List<String[]>.

// Reading All Records at once into a List<String[]>
List<String[]> records = csvReader.readAll();
for (String[] record : records) {
    System.out.println("Name : " + record[0]);
    System.out.println("Email : " + record[1]);
    System.out.println("Phone : " + record[2]);
    System.out.println("Country : " + record[3]);
    System.out.println("---------------------------");
}

Note that, the above method loads the entire CSV contents into memory, and therefore is not suitable for large CSV files.

Skip Header Row

If you try to read the Sample CSV file that contains a header, then the header record will also be printed in the output. If you want to skip the header row, then you can use a CSVReaderBuilder class to construct a CSVReader with the specified number of lines skipped.

import com.opencsv.CSVReaderBuilder;

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

Read a CSV file and parse the records into a Java Object

The real strength of OpenCSV library is that you can directly parse CSV records into Java objects. There are two ways of doing it - The first method makes use of annotations and the second method uses Mapping strategies.

There are two types of annotations in OpenCSV - @CsvBindByName and @CsvBindByPosition. You can use these annotations to specify which CSV column should be bound to which member field of the Java object.

If the CSV file contains a header, then you can use @CsvBindByName annotation to specify the mapping between the CSV columns and the member fields.

The @CsvBindByName annotation accepts three parameters - column, required and locale. The required and locale parameters are optional, and you can omit the column parameter as well if the header name in the CSV file is same as the member field name.

Here is an example of a POJO class that makes use of @CsvBindByName annotations -

import com.opencsv.bean.CsvBindByName;

public class CSVUser {
    @CsvBindByName
    private String name;

    @CsvBindByName(column = "email", required = true)
    private String email;

    @CsvBindByName(column = "phone")
    private String phoneNo;

    @CsvBindByName
    private String country;

	// Getters and Setters (Omitted for brevity)	
}

The example below shows how to read and parse the CSV records directly into your Java objects -

import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;

public class OpenCSVReadAndParseToBean {
    private static final String SAMPLE_CSV_FILE_PATH = "./users-with-header.csv";

    public static void main(String[] args) throws IOException {
        try (
            Reader reader = Files.newBufferedReader(Paths.get(SAMPLE_CSV_FILE_PATH));
        ) {
            CsvToBean<CSVUser> csvToBean = new CsvToBeanBuilder(reader)
                    .withType(CSVUser.class)
                    .withIgnoreLeadingWhiteSpace(true)
                    .build();

            Iterator<CSVUser> csvUserIterator = csvToBean.iterator();

            while (csvUserIterator.hasNext()) {
                CSVUser csvUser = csvUserIterator.next();
                System.out.println("Name : " + csvUser.getName());
                System.out.println("Email : " + csvUser.getEmail());
                System.out.println("PhoneNo : " + csvUser.getPhoneNo());
                System.out.println("Country : " + csvUser.getCountry());
                System.out.println("==========================");
            }
        }
    }
}

In the above example, we obtained an Iterator from csvToBean object, and then looped through this iterator to retrieve every object one by one.

The CsvToBean class also provides a parse() method which parses the entire CSV file and loads all the objects at once into memory. You can use it like so -

// Reads all CSV contents into memory (Not suitable for large CSV files)
List<CSVUser> csvUsers = csvToBean.parse();

for(CSVUser csvUser: csvUsers) {
    System.out.println("Name : " + csvUser.getName());
    System.out.println("Email : " + csvUser.getEmail());
    System.out.println("PhoneNo : " + csvUser.getPhoneNo());
    System.out.println("Country : " + csvUser.getCountry());
    System.out.println("==========================");
}

Obviously, the above method is not suitable for significantly large CSV files because it loads the entire CSV file contents into memory.

Using @CsvBindByPosition annotation

If your CSV file doesn’t contain a header, then you can use @CsvBindByPosition annotation to specify the mappings like this -

import com.opencsv.bean.CsvBindByPosition;

public class CSVUser {
    @CsvBindByPosition(position = 0)
    private String name;

    @CsvBindByPosition(position = 1)
    private String email;

    @CsvBindByPosition(position = 2)
    private String phoneNo;

    @CsvBindByPosition(position = 3)
    private String country;

    // Getters and Setters (Omitted for brevity)    
}

Read a CSV file and parse the records into a Java object without using annotations

If you don’t want to clutter your POJO class with OpenCSV annotations, then you can use Mapping strategies to specify the mapping between CSV columns and object member fields.

Consider the following MyUser class.

public class MyUser {
    private String name;
    private String email;
    private String phoneNo;
    private String country;

    public MyUser() {

    }

    public MyUser(String name, String email, String phoneNo, String country) {
        this.name = name;
        this.email = email;
        this.phoneNo = phoneNo;
        this.country = country;
    }
	
	// Getters and Setters (Omitted for brevity)
}

Here is how you can use a ColumnPositionMappingStrategy to specify the mapping between CSV columns and Java object’s member fields, and parse the CSV records into Java objects.

import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;

import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;

public class OpenCSVParseToBeanWithoutAnnotation {
    private static final String SAMPLE_CSV_FILE_PATH = "./users-with-header.csv";

    public static void main(String[] args) throws IOException {
        try (
            Reader reader = Files.newBufferedReader(Paths.get(SAMPLE_CSV_FILE_PATH));
        ) {
            ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();
            strategy.setType(MyUser.class);
            String[] memberFieldsToBindTo = {"name", "email", "phoneNo", "country"};
            strategy.setColumnMapping(memberFieldsToBindTo);

            CsvToBean<MyUser> csvToBean = new CsvToBeanBuilder(reader)
                    .withMappingStrategy(strategy)
                    .withSkipLines(1)
                    .withIgnoreLeadingWhiteSpace(true)
                    .build();

            Iterator<MyUser> myUserIterator = csvToBean.iterator();

            while (myUserIterator.hasNext()) {
                MyUser myUser = myUserIterator.next();
                System.out.println("Name : " + myUser.getName());
                System.out.println("Email : " + myUser.getEmail());
                System.out.println("PhoneNo : " + myUser.getPhoneNo());
                System.out.println("Country : " + myUser.getCountry());
                System.out.println("---------------------------");
            }
        }
    }
}

The ColumnPositionMappingStrategy is used to declare position based mapping. In the above example, we have bound the first column to name field, the second column to email field and so on…

Generating a CSV file

You can generate a CSV file either from an array of Strings or from a List of objects.

Generate CSV file from Array of Strings

The example below shows how to generate a CSV file by writing an Array of Strings into each row of the CSV file.

import com.opencsv.CSVWriter;

import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;

public class OpenCSVWriter {
    private static final String STRING_ARRAY_SAMPLE = "./string-array-sample.csv";

    public static void main(String[] args) throws IOException {
        try (
            Writer writer = Files.newBufferedWriter(Paths.get(STRING_ARRAY_SAMPLE));

            CSVWriter csvWriter = new CSVWriter(writer,
                    CSVWriter.DEFAULT_SEPARATOR,
                    CSVWriter.NO_QUOTE_CHARACTER,
                    CSVWriter.DEFAULT_ESCAPE_CHARACTER,
                    CSVWriter.DEFAULT_LINE_END);
        ) {
            String[] headerRecord = {"Name", "Email", "Phone", "Country"};
            csvWriter.writeNext(headerRecord);

            csvWriter.writeNext(new String[]{"Sundar Pichai ♥", "sundar.pichai@gmail.com", "+1-1111111111", "India"});
            csvWriter.writeNext(new String[]{"Satya Nadella", "satya.nadella@outlook.com", "+1-1111111112", "India"});
        }
    }
}

Generate CSV file from List of Objects

Finally, following is an example showing how to generate a CSV file from List of objects. The example uses the MyUser class defined in the previous section -

import com.opencsv.CSVWriter;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.opencsv.exceptions.CsvDataTypeMismatchException;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;

import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class OpenCSVWriter {
    private static final String OBJECT_LIST_SAMPLE = "./object-list-sample.csv";

    public static void main(String[] args) throws IOException,
            CsvDataTypeMismatchException,
            CsvRequiredFieldEmptyException {

        try (
            Writer writer = Files.newBufferedWriter(Paths.get(STRING_ARRAY_SAMPLE));
        ) {
            StatefulBeanToCsv<MyUser> beanToCsv = new StatefulBeanToCsvBuilder(writer)
                    .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
                    .build();

            List<MyUser> myUsers = new ArrayList<>();
            myUsers.add(new MyUser("Sundar Pichai ♥", "sundar.pichai@gmail.com", "+1-1111111111", "India"));
            myUsers.add(new MyUser("Satya Nadella", "satya.nadella@outlook.com", "+1-1111111112", "India"));

            beanToCsv.write(myUsers);
        }
    }
}

Conclusion

That’s all folks! In this article, We looked at different ways of reading and writing CSV files in Java using OpenCSV library.

You can find all the code samples presented in this article in my github repository. Consider giving the repository a star on github if you find it useful.

Thank you for reading. See you in the next post.