Learn how to Parse CSV File with OpenCSV
What is a CSV file
CSV stands for comma-separated values which are used to store data in a simple tabular format like a spreadsheet or database. CSV files use a delimiter to separate data in a file. By default, the delimiter is ",".
CSV can be created and exported using Excel or any tool that works with tables.
OpenCSV
OpenCSV is a simple API to read and write CSV files. This library provides functions to handle a CSV file.
OpenCSV Maven Dependency
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.5.2</version>
</dependency>
Below is a sample CSV file we will be using throughout this article.
Name,Address,joinedDate,salary,skils
Bhrathkumar,Chennai,20-05-2019,25000.00,java-html-php
Pavi,kerala,14-11-2022,22500.55,javascript-angular
John,UK,05-05-2005,10000.00,android
Reading CSV using CSVReader
The OpenCSV provides CSVReader to read CSV files, which takes the file as an argument. Our CSV file is located in the src/main/resources folder.
CSVReader provides various methods to read CSV files.
- readAll() → read the entire file into a list of string arrays where each array represents the row in the CSV.
- readNext() → read each line one by one till it reaches the end.
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import com.opencsv.CSVParser;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.exceptions.CsvException;
public class CSVExample {
public static void main(String[] args) {
try {
FileReader fileReader = new FileReader("src/main/resources/samplecsv.csv");
CSVReader reader = new CSVReader(fileReader);
reader.readAll().stream()
.forEach(line -> System.out.println(Arrays.toString(line)));
String[] line;
while (reader.readNext() != null) {
line = reader.readNext();
System.out.println("Line" + Arrays.toString(line));
}
} catch (IOException e) {
e.printStackTrace();
} catch (CsvException e) {
e.printStackTrace();
}
}
}
Changing the separator
We can also configure our reader to use a different separator. Below, we're using a "-" separator.
FileReader fileReader = new FileReader("src/main/resources/samplecsv.csv");
CSVParser parseBuilder = new CSVParserBuilder().withSeparator('-').build();
CSVReader reader = new CSVReaderBuilder(fileReader).withCSVParser(parseBuilder).build();
Skipping the Header column
We can skip the header column by using skip() functions. We have to tell how many rows we want to ignore here. We don’t want the header row so skip 1.
CSVReader reader = new CSVReader(fileReader);
reader.skip(1);
reader.readAll().stream().forEach(line -> System.out.println(Arrays.toString(line)));
Converting CSV to Java object using CSVReader
Using the CSVReader, we can get CSV data in lines. Now we want to map these values to a Java object.
The main problem is that if the class contains simple fields, then we can directly assign values. However, if the class contains lots of fields and collections, then we have to write the mapping implementation ourselves. This leads to bugs.
In the below code, we have converted our CSV name to a list of employees, passing employee name and address as parameters when creating an employee object.
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import com.opencsv.CSVParser;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.exceptions.CsvException;
public class CSVExample {
public static void main(String[] args) {
try {
FileReader fileReader = new FileReader("src/main/resources/samplecsv.csv");
CSVParser parseBuilder = new CSVParserBuilder().withSeparator('-').build();
CSVReader reader = new CSVReaderBuilder(fileReader).withCSVParser(parseBuilder).build();
List<Employee> employees = reader.readAll().stream()
.map(line -> createEmployee(line)).collect(Collectors.toList());
reader.close();
} catch (IOException e) {
e.printStackTrace();
} catch (CsvException e) {
e.printStackTrace();
}
}
public static Employee createEmployee(String[] line) {
return new Employee(line[0], line[1]);
}
}
Converting CSV to java object using annotations
We don’t have to manually write code to convert CSV to Java objects instead, CsvToBean does it for us.
In the below code, we have defined CsvToBean with class type as an employee so our CSV data will be converted to employee objects.
The parse() function will return a list of employees, converting each line in CSV to an employee.
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.List;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
public class CSVReaderUsingAnnotation {
public static void main(String[] args) {
try {
FileReader fileReader = new FileReader("src/main/resources/samplecsv.csv");
CsvToBean beanParser = new CsvToBeanBuilder(fileReader).withType(Employee.class).build();
List<Employee> employee = beanParser.parse();
System.out.println(employee);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
In employee class, we have to annotate fields and that's it. Below we have explained all the annotations that can be used in the model class when converting CSV to objects.
Annotation-based CSV conversion
OpenCSV provides annotations that can be used to read and transform CSV files into Java objects. There are different annotations to map CSV columns to java fields in CSV. We will see them in detail.
Header based mapping
A CSV file may have a header containing the names of columns we can map using this header name to the java field by annotating the field with @CsvBindByName.
Name,Address -> header columns
Bhrathkumar,Chennai
Pavi,kerala
John,UK
Column name and field name must match if not, use column attributes to specify the column names in @CsvBindByName.
import com.opencsv.bean.CsvBindByName;
import lombok.Data;
@Data
public class Employee {
@CsvBindByName
private String name;
@CsvBindByName
private String address;
}
If some column is not required when converting to an object we can tell that by specifying required attributes as false in @CsvBindByName by default it’s true.
@CsvBindByName(required = false)
Colum Position based mapping
If our CSV does not have a header, we can use the column position to map the values by annotating @CsvBindByPosition.
The @CsvBindByPosition takes the column position. For example, column 0 contains the names and column 1 contains the address.
import com.opencsv.bean.CsvBindByPosition;
import lombok.Data;
@Data
public class Employee {
@CsvBindByPosition(position = 0)
private String name;
@CsvBindByPosition(position = 1)
private String address;
}
Date mapping in CSV
Now we will see how to map date type in CSV to java field. OpenCSV provides @CsvDate to handle date mapping which takes an input pattern.
Name,Address,joinedDate
Bhrathkumar,Chennai,20-05-2019
Pavi,kerala,14-11-2022
John,UK,05-05-2005
@CsvDate annotations need to be used with @CsvBindByPosition or @CsvBindByName.
In the below code, we have a date field called joinedDate of pattern “dd-MM-yyyy”
import java.time.LocalDate;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvDate;
import lombok.Data;
@Data
public class Employee {
@CsvBindByName
private String name;
@CsvBindByName
private String address;
@CsvBindByName
@CsvDate("dd-MM-yyyy")
private LocalDate joinedDate;
}
Collections in CSV
We have seen how to map fields like string, data, that all have a single value. However, sometimes the CSV file can also have more than one value in the same column.
In this case, we can use collections to store them. The @CsvBindAndSplitByName function helps us map a group of values in a column to collections of values in an object.
In the below CSV, the skills column contains information about employees' skills. Values are delimited by a "-".
Name,Address,joinedDate,salary,skils
Bhrathkumar,Chennai,20-05-2019,25000.00,java-html-php
Pavi,kerala,14-11-2022,22500.55,javascript-angular
John,UK,05-05-2005,10000.00,android
@CsvBindAndSplitByName takes different attributes.
- elementType - define what type of element this collection is going to store.
- collectionsType - define the type of collection.
- spliton - using which we want to split the value and store them in the collection. Here we split by "-”.
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import com.opencsv.bean.CsvBindAndSplitByName;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvDate;
import lombok.Data;
@Data
public class Employee {
@CsvBindByName
private String name;
@CsvBindByName
private String address;
@CsvBindByName
@CsvDate("dd-MM-yyyy")
private LocalDate joinedDate;
@CsvBindByName
private double salary;
@CsvBindAndSplitByName(elementType = String.class, collectionType = ArrayList.class, splitOn = "-")
private List<String> skils;
}