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.
Upgrading from 2.x
Section titled “Upgrading from 2.x”This section describes the breaking changes when migrating from FastCSV 2.x to 3.x.
Requirement changes
Section titled “Requirement changes”- 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)
New limitations
Section titled “New limitations”- 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
.
Naming changes
Section titled “Naming changes”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");}
Method names
Section titled “Method names”- In
CsvReaderBuilder
:skipEmptyRows
is nowskipEmptyLines
errorOnDifferentFieldCount
is nowignoreDifferentFieldCount
(opposite meaning!)build
methods with callback handlers andofCsvRecord
/ofNamedCsvRecord
as convenience methods
- In
CsvRecord
(formerCsvRow
):getOriginalLineNumber
is nowgetStartingLineNumber
NamedCsvReader removed/replaced
Section titled “NamedCsvReader removed/replaced”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")));
Extended/Refactored quote strategies
Section titled “Extended/Refactored quote strategies”QuoteStrategy
changed from an enum to an interface (seeQuoteStrategies
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);
Exception changes
Section titled “Exception changes”MalformedCsvException
is now CsvParseException
and is thrown instead of IOException
for non-IO related errors.
Upgrading from 1.x
Section titled “Upgrading from 1.x”This section describes the breaking changes when migrating from FastCSV 1.x to 3.x.
Reader
Section titled “Reader”Configuring the reader
Section titled “Configuring the reader”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);
Reading data from file
Section titled “Reading data from file”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)) );}
Reading data with a header from file
Section titled “Reading data with a header from file”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")) );}
Read an entire file at once
Section titled “Read an entire file at once”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();}
Writer
Section titled “Writer”Configuring the writer
Section titled “Configuring the writer”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);
Write to file
Section titled “Write to file”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");}
Write to writer
Section titled “Write to writer”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");}