Writing a CSV File with Java Using OpenCSV

Comma-Separated Values (CSV) is a simple and widely used format for storing tabular data. Java developers often need to read from and write to CSV files in various applications, from data migration to analytics. OpenCSV is a popular and user-friendly library that simplifies handling CSV files in Java.

This article will explore how to write a CSV file using OpenCSV, including setting up dependencies, writing basic and complex data structures, handling special characters, and implementing best practices.

1. Setting Up OpenCSV in Your Java Project

To use OpenCSV, you need to add the library to your project. If you are using Maven, add the following dependency to your pom.xml:

<!-- https://mvnrepository.com/artifact/com.opencsv/opencsv -->
<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.10</version>
</dependency>

2. Writing a Basic CSV File

The CSVWriter class in OpenCSV makes it easy to write data to a CSV file. Below is a simple example of writing basic data to a CSV file.

Example:

import com.opencsv.CSVWriter;

import java.io.FileWriter;
import java.io.IOException;

public class WriteCSVExample {
    public static void main(String[] args) {
        String csvFile = "example.csv";

        try (CSVWriter writer = new CSVWriter(new FileWriter(csvFile))) {
            // Writing a single row
            String[] header = {"ID", "Name", "Age", "Country"};
            writer.writeNext(header);

            // Writing multiple rows
            String[] row1 = {"1", "Alice", "28", "USA"};
            String[] row2 = {"2", "Bob", "35", "UK"};
            String[] row3 = {"3", "Charlie", "40", "Canada"};

            writer.writeNext(row1);
            writer.writeNext(row2);
            writer.writeNext(row3);

            System.out.println("CSV file created successfully!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

  1. Create a FileWriter object pointing to the CSV file.
  2. Initialize a CSVWriter with the FileWriter.
  3. Use writeNext() to write rows to the CSV file.
  4. Close the writer using a try-with-resources block to ensure proper resource management.

The CSV file contains data.

"ID","Name","Age","Country"
"1","Alice","28","USA"
"2","Bob","35","UK"
"3","Charlie","40","Canada"

3. Writing Complex Data Structures

If you need to write a list of objects (e.g., Java Beans) to a CSV file, OpenCSV provides an easy way to do so.

3.1 Example: Writing a List of Java Objects to CSV

First, create a Java Bean representing the data structure:

import com.opencsv.bean.CsvBindByName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class EmployeeBean {
    @CsvBindByName(column = "ID")
    private int id;

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

    @CsvBindByName(column = "Age")
    private int age;

    @CsvBindByName(column = "Country")
    private String country;
}

Now, write a list of employees to a CSV file:

import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.opencsv.exceptions.CsvDataTypeMismatchException;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
import lombok.extern.slf4j.Slf4j;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class WriteBeanCSV {
    public static void main(String[] args) {
        String csvFile = "D:/logs/employees.csv";

        List<EmployeeBean> employees = Arrays.asList(
                new EmployeeBean(1, "Alice", 28, "USA"),
                new EmployeeBean(2, "Bob", 35, "UK"),
                new EmployeeBean(3, "Charlie", 40, "Canada")
        );

        try (FileWriter writer = new FileWriter(csvFile)) {
            StatefulBeanToCsv<EmployeeBean> beanToCsv = new StatefulBeanToCsvBuilder<EmployeeBean>(writer).build();
            beanToCsv.write(employees);
            log.info("CSV file created successfully!");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (CsvRequiredFieldEmptyException e) {
            throw new RuntimeException(e);
        } catch (CsvDataTypeMismatchException e) {
            throw new RuntimeException(e);
        }
    }
}

Explanation:

  1. Create a Java Bean with annotations (@CsvBindByName) to specify column mapping.
  2. Use StatefulBeanToCsv to convert Java objects into CSV format.
  3. Pass a list of objects to beanToCsv.write(employees);.

CSV file content data.

"AGE","COUNTRY","ID","NAME"
"28","USA","1","Alice"
"35","UK","2","Bob"
"40","Canada","3","Charlie"

3. 2 Example: Writing and Reading CSV with @CsvBindByPosition

import com.opencsv.bean.CsvBindByPosition;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class EmployeeBean {

    @CsvBindByPosition(position = 0)
    private int id;

    @CsvBindByPosition(position = 1)
    private String name;

    @CsvBindByPosition(position = 2)
    private int age;

    @CsvBindByPosition(position = 3)
    private String country;

}

Issue @CsvBindByPosition

The resulting content file does not include a header when writing to a file using the CsvBindByPosition annotation.

3.3 Example: Combine @CsvBindByPosition and @CsvBindByName

A developer can create a file with a header by using the CsvBindByPosition and CsvBindByName annotations.

import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class EmployeeBean {

    @CsvBindByPosition(position = 0)
    @CsvBindByName(column = "ID")
    private int id;

    @CsvBindByPosition(position = 1)
    @CsvBindByName(column = "Name")
    private String name;

    @CsvBindByPosition(position = 2)
    @CsvBindByName(column = "Age")
    private int age;

    @CsvBindByPosition(position = 3)
    @CsvBindByName(column = "Country")
    private String country;

}

Write a list of employees to a CSV file:

public static void main(String[] args) {
    File filePath = new File("employees.csv");
    List<EmployeeBean> employees = Arrays.asList(
            new EmployeeBean(1, "Alice", 28, "USA"),
            new EmployeeBean(2, "Bob", 35, "UK"),
            new EmployeeBean(3, "Charlie", 40, "Canada")
    );

    try (FileWriter writer = new FileWriter(filePath, StandardCharsets.UTF_8)) {
        writer.append(buildHeader(EmployeeBean.class));
        StatefulBeanToCsv<EmployeeBean> beanToCsv = new StatefulBeanToCsvBuilder<EmployeeBean>(writer).build();
        beanToCsv.write(employees);
        log.info("generated successfully");
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
}

public static String buildHeader(Class clazz) {
    return Arrays.stream(clazz.getDeclaredFields())
            .filter(f -> f.getAnnotation(CsvBindByPosition.class) != null
                    && f.getAnnotation(CsvBindByName.class) != null)
            .sorted(Comparator.comparing(f -> f.getAnnotation(CsvBindByPosition.class).position()))
            .map(f ->  "\"" + f.getAnnotation(CsvBindByName.class).column() + "\"")
            .collect(Collectors.joining(",")) + "\n";
}

When writing to a file using the CsvBindByPosition and CsvBindByName annotations, the resulting content file is ordered by position and includes a header.

3.4 Example: Writing a CSV file without double quotes

 // Configure OpenCSV to disable double quotes
StatefulBeanToCsv<EmployeeBean> beanToCsv = new StatefulBeanToCsvBuilder<EmployeeBean>(writer)
        .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER) // Disable double quotes
        .build();

3.5 Example: CsvBindByPosition using required argument

    @CsvBindByPosition(position = 1, required = true)
    @CsvBindByName(column = "Name")
    private String name;

Error message when writing a CSV file.

Exception in thread "pool-1-thread-1" java.lang.RuntimeException: com.opencsv.exceptions.CsvRequiredFieldEmptyException: Field 'name' is mandatory but no value was provided.
 at com.opencsv.bean.util.OpencsvUtils.handleException(OpencsvUtils.java:128)
 at com.opencsv.bean.concurrent.ProcessCsvBean.run(ProcessCsvBean.java:82)
 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
 at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: com.opencsv.exceptions.CsvRequiredFieldEmptyException: Field 'name' is mandatory but no value was provided.
 at com.opencsv.bean.AbstractBeanField.write(AbstractBeanField.java:339)
 at com.opencsv.bean.AbstractMappingStrategy.transmuteBean(AbstractMappingStrategy.java:668)
 at com.opencsv.bean.concurrent.ProcessCsvBean.run(ProcessCsvBean.java:78)
 ... 3 more

3.6 Example: Writing and Reading CSV with Custom Separator & No Quotes

StatefulBeanToCsv<EmployeeBean> beanToCsv = new StatefulBeanToCsvBuilder<EmployeeBean>(writer)
        .withSeparator(';')  // Use Semicolon(;) instead of comma(,)
        .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)  // No quotes
        .build();

3.7 Example: Using Custom Converters for Data Transformation

Define a Custom LocalDate to String Converter

import com.opencsv.bean.AbstractBeanField;
import com.opencsv.exceptions.CsvDataTypeMismatchException;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class LocalDateToStringConverter extends AbstractBeanField<LocalDate, String> {
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Override
    protected Object convert(String value) {
        return value == null || value.isEmpty() ? null : LocalDate.parse(value, formatter);
    }

    @Override
    protected String convertToWrite(Object value) throws CsvDataTypeMismatchException {
        return value == null ? "" : ((LocalDate) value).format(formatter);
    }
}

Apply Converter in the Model Class

import com.opencsv.bean.CsvBindByPosition;
import com.opencsv.bean.CsvCustomBindByPosition;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;

@Getter
@Setter
@AllArgsConstructor
public class EmployeeWithDate {
    @CsvBindByPosition(position = 0)
    private int id;

    @CsvBindByPosition(position = 1)
    private String name;

    @CsvCustomBindByPosition(position = 2, converter = LocalDateToStringConverter.class)
    private LocalDate hireDate;

}

Writing CSV with LocalDate Conversion

import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.opencsv.CSVWriter;

import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

public class CsvDateWriter {
    public static void main(String[] args) {
        String csvFile = "D:/logs/employees.csv";

        List<EmployeeWithDate> employees = Arrays.asList(
            new EmployeeWithDate(1, "Alice", LocalDate.of(2023, 1, 15)),
            new EmployeeWithDate(2, "Bob", LocalDate.of(2022, 6, 10)),
            new EmployeeWithDate(3, "Charlie", LocalDate.of(2021, 12, 5))
        );

        try (FileWriter writer = new FileWriter(csvFile)) {
            // Write header manually
            writer.append("ID,Name,HireDate\n");

            StatefulBeanToCsv<EmployeeWithDate> beanToCsv = new StatefulBeanToCsvBuilder<EmployeeWithDate>(writer)
                    .withSeparator(',')  // Use comma separator
                    .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)  // No quotes
                    .build();

            beanToCsv.write(employees);
            System.out.println("CSV file written successfully with formatted LocalDate!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Expected Output (employees.csv)

ID,Name,HireDate
1,Alice,2023-01-15
2,Bob,2022-06-10
3,Charlie,2021-12-05

Dates are now formatted as dd-MM-yyyy before being written to CSV.

4. Handling Special Characters in CSV

CSV files often contain special characters such as commas, quotes, and newlines. OpenCSV provides solutions to handle these cases.

Handling Commas and Quotes:

By default, OpenCSV automatically wraps values in double quotes "
If they contain a comma.

Example:

String[] row = {"4", "John, Doe", "30", "\"Germany\""};
writer.writeNext(row);

This will be written as:

4,"John, Doe",30,"Germany"

Customizing Quote and Escape Characters:

You can customize delimiters and escape characters:

CSVWriter writer = new CSVWriter(new FileWriter("custom.csv"),
    '|',    // Custom delimiter (Pipe `|`)
    '\'',   // Quote character (Single Quote `'`)
    '\\',   // Escape character (`\`)
    "\n");  // Line separator

This changes the output format to:

'4'|'John, Doe'|'30'|'Germany'

5. Best Practices for Writing CSV in Java

To ensure efficient and error-free CSV handling, follow these best practices:

  1. Use Try-with-Resources: This ensures the FileWriter and CSVWriter are closed properly, preventing memory leaks.
  2. Validate Data Before Writing: Ensure data integrity by validating input values before writing them to CSV.
  3. Handle Exceptions Gracefully: Catch and log IOException to prevent unexpected failures.
  4. Use a BufferedWriter for Large Files: This improves large datasets.
  5. Choose an Appropriate Delimiter: If your data contains commas, consider using a different delimiter, like | or ;.
  6. Escape Special Characters: Use OpenCSV’s built-in escaping mechanisms to handle quotes and line breaks.

Alternative Libraries

  • Apache Commons CSV — Another flexible CSV library for Java.
  • Super CSV — Feature-rich but with a steeper learning curve.
  • Jackson CSV — Best if you’re already using Jackson for JSON processing.

Finally

OpenCSV is a powerful and easy-to-use library for writing CSV files in Java. We covered:

  • Writing essential CSV files using CSVWriter
  • Writing complex objects using StatefulBeanToCsv
  • Handling special characters and customizing delimiters
  • Best practices for efficient CSV handling

Following these guidelines, you can easily integrate CSV writing functionality into your Java applications. OpenCSV simplifies CSV handling, making it an excellent choice for small- and large-scale projects.

This article was originally published on Medium.

Leave a Comment

Your email address will not be published. Required fields are marked *