Skip to content

Upgrading

This document describes the breaking changes when upgrading from earlier versions of FastCSV to 3.x. For a full list of changes, including new features, see the changelog.

This section describes the breaking changes when migrating from FastCSV 2.x to 3.x.

  • The minimum Java version is now 11 (compared to 8 in earlier versions)
  • This also raised the required Android API level from version 26 (Android 8) to 33 (Android 13)
  • The maximum number of fields per record is now limited to 16,384 to avoid OutOfMemoryErrors caused by excessive field counts.
  • The maximum record size is now limited to (64 MiB) to prevent OutOfMemoryErrors.

Rows are now called Records (aligned to RFC 4180)

Section titled “Rows are now called Records (aligned to RFC 4180)”

Reading CSV records:

try (CsvReader<CsvRecord> csv = CsvReader.builder().ofCsvRecord(file)) {
csv.forEach(System.out::println);
}

Write CSV records:

try (CsvWriter csv = CsvWriter.builder().build(file)) {
csv
.writeRecord("header1", "header2")
.writeRecord("value1", "value2");
}
  • In CsvReaderBuilder:
    • skipEmptyRows is now skipEmptyLines
    • errorOnDifferentFieldCount is now ignoreDifferentFieldCount (opposite meaning!)
    • build methods with callback handlers and ofCsvRecord / ofNamedCsvRecord as convenience methods
  • In CsvRecord (former CsvRow):
    • getOriginalLineNumber is now getStartingLineNumber

A distinct NamedCsvReader is no longer needed as the CsvReader now supports callbacks for header and record processing.

CsvReader.builder().ofNamedCsvRecord("header 1,header 2\nfield 1,field 2")
.forEach(rec -> System.out.println(rec.getField("header2")));

or with a custom header:

NamedCsvRecordHandler callbackHandler = NamedCsvRecordHandler.builder()
.header("header1", "header2")
.build();
CsvReader.builder().build(callbackHandler, "field 1,field 2")
.forEach(rec -> System.out.println(rec.getField("header2")));
  • QuoteStrategy changed from an enum to an interface (see QuoteStrategies for the default implementations)
  • The REQUIRED quote strategy is removed as it is the default (no quote strategy is needed)

Example to enable always quoting:

CsvWriter.builder()
.quoteStrategy(QuoteStrategies.ALWAYS);

MalformedCsvException is now CsvParseException and is thrown instead of IOException for non-IO related errors.

This section describes the breaking changes when migrating from FastCSV 1.x to 3.x.

Old way:

CsvReader csvReader = new CsvReader();
csvReader.setFieldSeparator(',');
csvReader.setTextDelimiter('"');
csvReader.setSkipEmptyRows(true);
csvReader.setErrorOnDifferentFieldCount(false);

New way:

CsvReader.builder()
.fieldSeparator(',')
.quoteCharacter('"')
.skipEmptyLines(true)
.ignoreDifferentFieldCount(true);

Old way:

try (CsvParser csvParser = new CsvReader().parse(file, UTF_8)) {
CsvRow row;
while ((row = csvParser.nextRow()) != null) {
System.out.println("First field of row: " + row.getField(0));
}
}

New way:

try (CsvReader<CsvRecord> csv = CsvReader.builder().ofCsvRecord(file)) {
csv.forEach(rec ->
System.out.println("First field of record: " + rec.getField(0))
);
}

Old way:

CsvReader csvReader = new CsvReader();
csvReader.setContainsHeader(true);
try (CsvParser csvParser = csvReader.parse(file, UTF_8)) {
CsvRow row;
while ((row = csvParser.nextRow()) != null) {
System.out.println("Field named firstname: " + row.getField("firstname"));
}
}

New way:

try (CsvReader<NamedCsvRecord> csv = CsvReader.builder().ofNamedCsvRecord(file)) {
csv.forEach(rec ->
System.out.println("Field named firstname: " + rec.getField("firstname"))
);
}

Old way:

CsvContainer csv = new CsvReader().read(file, UTF_8);

New way:

The container concept has been removed, but you can easily build something similar using the Java Stream API.

List<CsvRecord> data;
try (CsvReader<CsvRecord> csvReader = CsvReader.builder().ofCsvRecord(file)) {
data = csvReader.stream().toList();
}

Old way:

CsvWriter csvWriter = new CsvWriter();
csvWriter.setFieldSeparator(',');
csvWriter.setTextDelimiter('"');
csvWriter.setAlwaysDelimitText(true);
csvWriter.setLineDelimiter(new char[]{'\r','\n'});

New way:

CsvWriter.builder()
.fieldSeparator(',')
.quoteCharacter('"')
.quoteStrategy(QuoteStrategies.ALWAYS)
.lineDelimiter(LineDelimiter.CRLF);

Old way:

try (CsvAppender csvAppender = new CsvWriter().append(file)) {
csvAppender.appendLine("header1", "header2");
csvAppender.appendLine("value1", "value2");
}

New way:

try (CsvWriter csvWriter = CsvWriter.builder().build(file)) {
csvWriter
.writeRecord("header1", "header2")
.writeRecord("value1", "value2");
}

Old way:

Writer writer = new StringWriter();
try (CsvAppender csvAppender = new CsvWriter().append(writer)) {
csvAppender.appendLine("header1", "header2");
csvAppender.appendLine("value1", "value2");
}

New way:

Writer writer = new StringWriter();
try (CsvWriter csvWriter = CsvWriter.builder().build(writer)) {
csvWriter
.writeRecord("header1", "header2")
.writeRecord("value1", "value2");
}