OpenCSV is a Java library that reads and writes CSV files, handling quoted values, commas inside fields and escaped quotes for us. It reads rows as String arrays or maps them straight to Java beans, and it writes arrays, beans or a JDBC ResultSet back to CSV.
We use OpenCSV to import and export data that spreadsheets and other systems exchange as CSV, such as product catalogs, bank transactions or report downloads. Splitting lines on commas by hand breaks on the first value that contains a comma, which is the problem OpenCSV solves.
The following example reads a small book list with OpenCSV 5.12.0, maps it to Book beans and writes one more row.
List<Book> books = new CsvToBeanBuilder<Book>(Files.newBufferedReader(Path.of("books.csv")))
.withType(Book.class)
.build()
.parse();
int count = books.size(); // 3
String second = books.get(1).getTitle(); // "Java, A Beginner's Guide"
StringWriter out = new StringWriter();
try (CSVWriter writer = new CSVWriter(out)) {
writer.writeNext(new String[]{"Dune", "Frank Herbert", "1965"});
}
List<String> line = out.toString().lines().toList(); // ["Dune","Frank Herbert","1965"]
Notice that the title with a comma inside comes through as one value, and that CSVWriter puts quotes around every field by default. We set up the dependency, read files line by line and as maps, map rows to beans, write and append files, and handle bad data, quotes and large files.
1. Setting Up
OpenCSV 5.12.0 is the current release. It supports Java 8 and later and runs on Java 25, and it pulls in Apache Commons Lang, Commons Text, Commons BeanUtils and Commons Collections.
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.12.0</version>
</dependency>
All examples read the following books.csv file. The second title contains a comma, so the CSV format requires quotes around it.
title,author,year
"Effective Java",Joshua Bloch,2018
"Java, A Beginner's Guide",Herbert Schildt,2024
Clean Code,Robert Martin,2008
2. Main Classes of the OpenCSV API
OpenCSV has a low-level API that works with String[] rows and a bean API on top of it. Most apps need the bean API for reading and writing typed objects, and the low-level classes for quick scripts or files without a fixed structure.
| Class | Use it for |
|---|---|
| CSVReader | Reading rows as String[] with readNext(), readAll() or a for-each loop |
| CSVReaderHeaderAware | Reading rows as Map<String, String> keyed by the header names |
| CSVReaderBuilder, CSVParserBuilder | Configuring skipped lines, separators, quote characters and the parser |
| CSVWriter | Writing String[] rows or a JDBC ResultSet |
| CsvToBeanBuilder | Mapping rows to beans with @CsvBindByName or @CsvBindByPosition |
| StatefulBeanToCsvBuilder | Writing a list of beans, including the header line |
The old BeanToCsv class and the setter-based CsvToBean setup from OpenCSV 3 and 4 were removed in version 5. Code that calls csv.setCsvReader() or new BeanToCsv() does not compile with OpenCSV 5, and the builders replace them.
3. Parsing and Reading a CSV File
A CSV parser walks through the text character by character and tracks whether it is inside quotes, so a comma or a line break inside quotes stays part of the value. OpenCSV returns each record as a String array, and the caller converts the values to numbers or dates.

3.1. Reading a CSV File Line by Line
Reading line by line keeps only one record in memory, so it works for files of any size. The readNext() method returns null at the end of the file, and it declares CsvValidationException for lines that break the configured rules.
List<String[]> rows = new ArrayList<>();
try (CSVReader reader = new CSVReaderBuilder(Files.newBufferedReader(Path.of("books.csv")))
.withSkipLines(1) // skip the header
.build()) {
String[] next;
while ((next = reader.readNext()) != null) {
rows.add(next);
}
}
String[] first = rows.get(0); // [Effective Java, Joshua Bloch, 2018]
int columns = rows.get(1).length; // 3, the quoted comma stays in the title
A CSVReader is also Iterable<String[]>, so a for-each loop over the reader does the same as the while loop. We open the file with Files.newBufferedReader(), which reads UTF-8 by default, and close it with try-with-resources.
For a file that uses another separator, such as the semicolon that European Excel versions write, we pass a parser to the builder.
String[] fields;
try (CSVReader reader = new CSVReaderBuilder(new StringReader("Dune;Frank Herbert;1965"))
.withCSVParser(new CSVParserBuilder().withSeparator(';').build())
.build()) {
fields = reader.readNext();
}
String[] semicolon = fields; // [Dune, Frank Herbert, 1965]
Avoid withIgnoreQuotations(true) unless the file has no quotes at all. With that option, the parser treats quotes as plain characters, so “Java, A Beginner’s Guide” splits into two values and the row gets four columns instead of three.
3.2. Reading the Complete CSV File at Once
The readAll() method returns every record as a List<String[]>. It is handy for small files such as a settings table, but it keeps the whole file in memory.
List<String[]> all;
try (CSVReader reader = new CSVReader(Files.newBufferedReader(Path.of("books.csv")))) {
all = reader.readAll();
}
int lines = all.size(); // 4, including the header
3.3. Reading Rows as Maps by Header Name
When the column order can change between file versions, positions are fragile. The CSVReaderHeaderAware class reads the first line as the header and returns each following row as a map from column name to value.
Map<String, String> row;
try (CSVReaderHeaderAware reader = new CSVReaderHeaderAware(Files.newBufferedReader(Path.of("books.csv")))) {
row = reader.readMap();
}
String author = row.get("author"); // "Joshua Bloch"
String year = row.get("year"); // "2018"
4. Reading CSV into Java Beans
The bean API maps each column to a field and converts the text to the field type, such as int, BigDecimal or LocalDate. We mark the fields with @CsvBindByName to match header names, or with @CsvBindByPosition for files without a header.
public static class Book {
@CsvBindByName(column = "title")
private String title;
@CsvBindByName(column = "author")
private String author;
@CsvBindByName(column = "year")
private int year;
public Book() {
}
public Book(String title, String author, int year) {
this.title = title;
this.author = author;
this.year = year;
}
public String getTitle() { return title; }
public String getAuthor() { return author; }
public int getYear() { return year; }
}
List<Book> books = new CsvToBeanBuilder<Book>(Files.newBufferedReader(Path.of("books.csv")))
.withType(Book.class)
.build()
.parse();
int year = books.get(0).getYear(); // 2018
String author = books.get(2).getAuthor(); // "Robert Martin"
Header matching ignores case, so Title and title both work. The bean needs a public no-argument constructor, which is why Java records do not work with CsvToBeanBuilder and fail with CsvBeanIntrospectionException. If the domain model uses records, we read into a small bean or into maps and convert.
The parse() method reads all rows into a list and processes them on several threads. For a large file, we iterate over the CsvToBean object instead, which creates one bean at a time on the calling thread.
List<String> titles = new ArrayList<>();
CsvToBean<Book> csvToBean = new CsvToBeanBuilder<Book>(Files.newBufferedReader(Path.of("books.csv")))
.withType(Book.class)
.build();
for (Book book : csvToBean) {
titles.add(book.getTitle());
}
String last = titles.get(2); // "Clean Code"
5. Writing into a CSV File
The CSVWriter class quotes values and escapes quotes inside values by doubling them. By default, it puts quotes around every field and ends each line with \n.
StringWriter out = new StringWriter();
try (CSVWriter writer = new CSVWriter(out)) {
writer.writeNext(new String[]{"Dune", "Frank Herbert", "1965"});
writer.writeNext(new String[]{"Java, Guide", "Herbert Schildt", "2024"}, false);
writer.writeNext(new String[]{"Say \"Hi\"", "Anna", null});
}
String csv = out.toString();
List<String> written = csv.lines().toList(); // ["Dune","Frank Herbert","1965", "Java, Guide",Herbert Schildt,2024, "Say ""Hi""","Anna",]
The second argument of writeNext(line, applyQuotesToAll) controls the quotes. With true (the default), every value is quoted. With false, only values that need quotes, such as Java, Guide with its comma, are quoted. A null value becomes an empty field.
To write to a file, we wrap a Writer from Files.newBufferedWriter() in the CSVWriter. Excel on Windows expects a byte order mark to detect UTF-8, so for files that users open in Excel, we write \uFEFF to the writer before the first row.
5.1. Writing Beans with StatefulBeanToCsv
The StatefulBeanToCsv class writes beans, header included. By default, it writes the header in upper case and sorts the columns alphabetically, which surprises most people. A HeaderColumnNameMappingStrategy with a column order fixes that.
HeaderColumnNameMappingStrategy<Book> strategy = new HeaderColumnNameMappingStrategy<>();
strategy.setType(Book.class);
List<String> order = List.of("TITLE", "AUTHOR", "YEAR");
strategy.setColumnOrderOnWrite(Comparator.comparingInt(order::indexOf));
StringWriter out = new StringWriter();
new StatefulBeanToCsvBuilder<Book>(out)
.withMappingStrategy(strategy)
.withApplyQuotesToAll(false)
.build()
.write(List.of(new Book("Dune", "Frank Herbert", 1965)));
List<String> beanLines = out.toString().lines().toList(); // [TITLE,AUTHOR,YEAR, Dune,Frank Herbert,1965]
The write() method declares CsvDataTypeMismatchException and CsvRequiredFieldEmptyException, so the calling method handles or declares them.
6. Appending to an Existing CSV File
OpenCSV has no append mode of its own, because appending is a property of the file stream. We open the file with StandardOpenOption.APPEND and write rows without a header.
Path copy = Files.copy(Path.of("books.csv"), Path.of("books-copy.csv"), StandardCopyOption.REPLACE_EXISTING);
try (CSVWriter writer = new CSVWriter(Files.newBufferedWriter(copy, StandardOpenOption.APPEND))) {
writer.writeNext(new String[]{"Refactoring", "Martin Fowler", "2018"}, false);
}
List<String> appended = Files.readAllLines(copy);
String lastLine = appended.get(appended.size() - 1); // "Refactoring,Martin Fowler,2018"
Appending works only if the existing file ends with a line break. Excel and many tools save the last row without one, and the new row is glued to the last line. The append to file article covers the file options in more detail.
7. Handling Bad Rows
A bank export with 50,000 transactions often has a few broken rows, for example an amount of n/a. By default, parse() stops at the first bad row with a RuntimeException that wraps a CsvDataTypeMismatchException, so one bad line rejects the whole file.
String data = "title,author,year\nX,Y,abc\nA,B,2001\n";
List<Book> strict = new CsvToBeanBuilder<Book>(new StringReader(data)).withType(Book.class).build().parse(); // RuntimeException: Error parsing CSV line: 2. [X,Y,abc], cause CsvDataTypeMismatchException
With withThrowExceptions(false), OpenCSV skips the bad rows and collects the errors, so the import can store the good rows and report the bad ones with their line numbers.
String data = "title,author,year\nX,Y,abc\nA,B,2001\n";
CsvToBean<Book> lenient = new CsvToBeanBuilder<Book>(new StringReader(data))
.withType(Book.class)
.withThrowExceptions(false)
.build();
List<Book> good = lenient.parse();
int imported = good.size(); // 1
long badLine = lenient.getCapturedExceptions().get(0).getLineNumber(); // 2
String reason = lenient.getCapturedExceptions().get(0).getMessage(); // "Conversion of abc to int failed."
8. Creating CSV from SQL Data
Exporting a database table is a common task, and CSVWriter.writeAll(ResultSet, includeHeaders) writes all rows of a JDBC ResultSet. With includeHeaders set to true, the column labels become the first line.
static void exportBooks(Connection con, Path target) throws SQLException, IOException {
try (Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT title, author, published FROM book");
CSVWriter writer = new CSVWriter(Files.newBufferedWriter(target))) {
writer.writeAll(rs, true);
}
}
Dates and timestamps are formatted with default patterns, which ResultSetHelperService.setDateFormat() and setDateTimeFormat() change. For millions of rows, we set a JDBC fetch size, so the driver does not load the whole result into memory first.
9. OpenCSV vs Other Ways to Parse CSV
Java has no CSV parser in the JDK. The CSV parsing overview compares the options in detail, and the short version is in this table.
| Option | Quotes and embedded commas | Beans | Good for |
|---|---|---|---|
| String.split(“,”) | No | No | Simple data we control, see splitting a CSV string |
| OpenCSV | Yes | Yes, with annotations | Imports and exports with typed beans |
| Apache Commons CSV | Yes | No | Fast record-based parsing with named formats such as CSVFormat.EXCEL |
| Jackson CSV (jackson-dataformat-csv) | Yes | Yes, including records | Apps that already use Jackson for JSON |
We pick OpenCSV when we want beans with annotations and little setup, and Commons CSV when we want a small, fast parser without bean mapping. For batch jobs, Spring Batch has its own FlatFileItemReader for CSV.
10. OpenCSV FAQs
The bean annotations and the quoting rules raise more questions than reading or writing itself.
10.1. Does OpenCSV support Java records?
No. CsvToBeanBuilder creates beans with a public no-argument constructor and sets the fields afterward, so a record fails with CsvBeanIntrospectionException. Jackson’s CSV module supports records, and with OpenCSV we can read into maps or a bean and convert.
10.2. How do I skip the header line?
With CSVReader, we use new CSVReaderBuilder(reader).withSkipLines(1). The bean API with @CsvBindByName uses the header for the mapping, so it skips the header without any setting.
10.3. How do I parse dates with OpenCSV?
We add @CsvDate(“yyyy-MM-dd”) next to the binding annotation on a LocalDate, LocalDateTime or Date field. Without @CsvDate, a date field fails with CsvDataTypeMismatchException.
10.4. Why does CSVWriter put quotes around every value?
Quoting all fields is the default, which is valid CSV and safe for any content. To quote only values that contain the separator, a quote or a line break, we call writeNext(line, false) or use withApplyQuotesToAll(false) on StatefulBeanToCsvBuilder.
10.5. Is OpenCSV RFC 4180 compliant?
The default CSVParser handles nearly all real files and treats a backslash as an escape character. For files that must follow RFC 4180 strictly, including backslashes as plain text, we use new RFC4180ParserBuilder().build() with withCSVParser().
11. Conclusion
OpenCSV reads CSV files as arrays, maps or beans and writes them back with correct quoting. The builders, namely CSVReaderBuilder, CsvToBeanBuilder and StatefulBeanToCsvBuilder, replace the older setter-based API and cover separators, skipped lines, column order and error collection.
For large files, we iterate instead of calling readAll() or parse(), and for messy data we collect the bad rows with withThrowExceptions(false). The bean API needs classic beans, so records go through a small adapter class.
12. References
Happy Learning !!
I am new to Selenium Webdriver Testng and trying to Develop Data Driven framework using CSVReader
I have 5 tests in Testng and have to run those all on different environments. So basically I have to pick a different url and client, but other pages, elements remain same.
My Question is, Where do I put while loop so that it reads first line and execute all the tests and then read second line and so on.
I tried initialize CSV reader at class level, before suite, before method etc. but it throws error, it works for a particular test if I am putting insside @Test but that doesn’t resolve the purpose, I couldn’t find any relevant answer on the internet.
Please note that for Android, mapping to a java bean does not work due to Android missing the class “java.beans.Introspector”. You can use reader.readAll() and then take it from there.
Open CSV is writing just the column headers on to the CSV file but not the table content. But while I’m printing the values, entire table is printing through my java program. How to fix this? Please help.
You can find my code here – https://stackoverflow.com/questions/39180736/opencsv-sql-writes-only-the-header-and-not-the-table-content
CSVReader reader = new CSVReader(new FileReader(“data.csv”), ‘,’ , ‘”‘ , 1);
reader.readNext() doesn’t work properly when one of the columns data has special characters as input such as below
“~!@#$%^&*()_+`[]{}|:””;,./{}|{}|+*&%#@~\”
Does anybody know how to handle csv columns parsing in this case?
Hi Lokesh,
Is there any solution for this question? I am also facing the same issue.
Hi Lokesh,
Is there any solution for this question? I am also facing the same issue.
Hi,
I don’t see in this tutorial and can’t find on the web any example of usage of BeanToCsv class.
Can someone provide this ?
Thanks
Good tutorial, seems pretty clearly organized. I would however, have 2 questions for you based on this material:
1. Would it possible to apply the same mapping from CSV directly to Java bean in case of having non-String fields? For example if the employee ID and the Employee age were declared integers, would the same mapping presented here be valid?
2. How would it be possible to export data using OpenCSV into HTML format?
1) I believe it should. Can you please try and confirm?
2) I am not sure, because OpenCSV is not for reporting purpose.
what is the parse method in class CSVMappedToJavaBeanExample.java as it is not defined in Employee class.
List list = csv.parse(setColumMapping(), csvReader);
Hello Sir,
This information is very useful. However, in my requirement, I need to ignore the occurrence of delimiter in the last string I get from CSV. Can you give an example how it could be done?
Thanks & regards,
Santhosh Kumar.
Not sure what you are looking for. Can you please give some sample CSV which you want to read/escape?
First Name;;; Last Name; City; Address;; zip code;; and this \n is the \n additional; text; as \n a last string
Thank you for your reply Lokesh. Consider the above row as text in the CSV file. In this case, using “;” as delimiter, I can get the strings. But in the last string mentioned in brackets, (and this \n is the \n additional; text; as \n a last string), I dont want the delimition to happen. This is what I am looking for. In this case, I cannot use “\” as escape character as \n would not work. Please help. Thanks in advance
You can do only those things with code which you logically implement in steps. In your case, probably you first need to use the delimiter ‘and’ or any such word. This will divide you string into two parts. You can again tokenize the first part with delimiter ‘;’ and leave the second part.
If you can not break your logic into logical steps, you will not be able to write the code for it as well. So please try to create logical steps first, and then let me know for which part you want me to help in writing code.
I hope I make sense.
Hi,
I would like to write mutiple resultsets into a csv file. How can I do that? Is there anyway I can write in next tabs?
Please advice.
Thanks,
Sivaprasath
Sir,
If my bean class has reference of other Class then how do I bind csv to my bean class, consider following example :
public class Employee{
private int empId;
private int empName;
private Address address;
}
public class Address {
private String city;
private String state;
}
And my Employee.csv file is like this :
EmpId,EmpName,City,State
101,xyz,abc,def
Please clarify
Tricky question. I will take some time to do some research on it.
Hi Lokesh,
Could you please tell that how we could populate Address in Employee object .
I need to populate Address values in address of Employee entity.
Thanks & Regards,
Raja Sekhar.
Can POI also read CSV files or just Excel?
Apache POI was never designed to call on CSV files. While a CSV File may be opened in Excel, Excel has its own reader that does an auto import. For CSV file, OpenCSV is better option.