Upgrading
This document describes the breaking changes when upgrading from FastCSV 3.x to 4.x. For a full list of changes, including new features, see the changelog.
Requirement changes
Section titled “Requirement changes”- The minimum Java version has been raised from 11 to 17
- This also raised the required Android API level from version 33 (Android 13) to 34 (Android 14)
Duplicate header handling
Section titled “Duplicate header handling”FastCSV 4.x rejects duplicate headers by default, ensuring that each header field is unique and preventing misinterpretation.
You can change this behavior by calling allowDuplicateHeaderFields(true)
on the NamedCsvRecordHandlerBuilder
.
var rh = NamedCsvRecordHandler.of(c -> c.allowDuplicateHeaderFields(true));try (CsvReader<NamedCsvRecord> csv = CsvReader.builder().build(rh, csvFile)) { // ...}
Ignoring different field counts
Section titled “Ignoring different field counts”FastCSV 4.x no longer ignores different field counts by default, ensuring that data is not misinterpreted.
You can change this behavior by calling allowExtraFields(true)
and allowMissingFields(true)
in the CsvReaderBuilder
.
These methods provide more control over how to handle different field counts in CSV data than the previous (now removed) ignoreDifferentFieldCount()
method.
CsvReaderBuilder builder = CsvReader.builder() .allowExtraFields(true) .allowMissingFields(true);
try (CsvReader<CsvRecord> csv = builder.ofCsvRecord(csvFile)) { // ...}
Internal buffer flushing
Section titled “Internal buffer flushing”In FastCSV 2.x and 3.x, the CsvWriter instantiated via CsvWriterBuilder.build(Writer)
flushed the internal buffer to the Writer
after each record.
This is no longer the case!
In FastCSV 4.x, the CsvWriter
for OutputStream
and Writer
behaves the same way:
it writes the internal buffer to the OutputStream
or Writer
only when the internal buffer is full, or when flush()
or close()
is called.
The buffer can also be completely disabled by calling CsvWriterBuilder.bufferSize(0)
.
As a consequence, you no longer need or should wrap the Writer
in a BufferedWriter
to ensure proper performance, unless you haven’t disabled FastCSV’s internal buffer (CsvWriterBuilder.bufferSize(0)
).
This change was made to align with the behavior of CsvWriter
for OutputStream
. When writing to an OutputStream
, a java.io.OutputStreamWriter
is used, which also buffers internally. Automatically flushing FastCSV’s internal buffer after each record would have no effect, as the OutputStreamWriter
would still buffer the data until it is full or flushed manually.
Summary, when dealing with Writer
or OutputStream
:
- Close the
CsvWriter
at the end of your writing process to ensure all data is written. - Call
flush()
on theCsvWriter
before writing to the underlyingWriter
orOutputStream
directly to ensure all data is written. - Calling
CsvWriterBuilder.bufferSize(0)
disables FastCSV’s internal buffer, but the buffer ofjava.io.OutputStreamWriter
cannot be disabled.
Handling of characters after closing quote
Section titled “Handling of characters after closing quote”FastCSV 4.x no longer tolerates characters after the closing quote of a quoted field, by default. This is a change in behavior compared to earlier versions of FastCSV. It ensures no erroneous data is read and interpreted without noticing it.
The following examples will now lead to a CsvParseException
:
"foo"INVALID,"bar""foo" ,"bar"
To restore the old behavior, you can use the allowExtraCharsAfterClosingQuote(boolean)
method in CsvReaderBuilder
or IndexedCsvReaderBuilder
.
This method was named acceptCharsAfterQuotes(boolean)
in earlier versions of FastCSV.
Quote strategies
Section titled “Quote strategies”Quote strategies received two breaking changes in FastCSV 4:
- The method
quoteNonEmpty
ofde.siegmar.fastcsv.writer.QuoteStrategy
has been renamed toquoteValue
to better reflect its purpose. - Two explicitly define that quoting only happens if required, the
quoteStrategy
method inCsvWriterBuilder
no longer accepts anull
value. Instead, you can use theQuoteStrategies.REQUIRED
constant to indicate that quoting should only happen if required.
Changed implementation of CsvIndex
and CsvPage
to Java records
Section titled “Changed implementation of CsvIndex and CsvPage to Java records”The CsvIndex
and CsvPage
classes have been changed to Java records. With this change, a few method calls have changed as well.
CsvIndex csvIndex = yourCodeToBuildTheIndex();
// Simple get-prefix removalcsvIndex.getBomHeaderLength();csvIndex.bomHeaderLength();
csvIndex.getFileSize();csvIndex.fileSize();
csvIndex.getFieldSeparator();csvIndex.fieldSeparator();
csvIndex.getQuoteCharacter();csvIndex.quoteCharacter();
csvIndex.getCommentStrategy();csvIndex.commentStrategy();
csvIndex.getCommentCharacter();csvIndex.commentCharacter();
csvIndex.getRecordCount();csvIndex.recordCount();
// Replaced getPageCount() with pages().size()csvIndex.getPageCount();csvIndex.pages().size();
// Replaced getPage(int) with direct access to the pages listvar firstPage = csvIndex.getPage(0);var firstPage = csvIndex.pages().getFirst();
// Again, simple get-prefix removalfirstPage.getOffset();firstPage.offset()
firstPage.getStartingLineNumber();firstPage.startingLineNumber();
Callback handler refactoring
Section titled “Callback handler refactoring”If you implemented a custom callback handler, some major changes have been made in FastCSV 4.0 that you need to be aware of:
Record wrapper removal: The RecordWrapper
class has been removed. Its getFieldCount
method has been moved to the CsvCallbackHandler
. The methods isComment
and isEmptyLine
have been combined into a single getRecordType
method that returns the type of the record (comment, empty line, or regular record). This getRecordType
method is now also part of the CsvCallbackHandler
class.
Handling empty lines: The CSV parser is emitting empty lines separately from comments and regular records.
If you implemented a custom callback handler by extending AbstractBaseCsvCallbackHandler
, your implementation for the buildRecord
method now needs to return a CsvRecord
instance instead of a RecordWrapper
. You may also want to implement the handleEmpty
method to handle empty lines suiting your needs.
Implementing the lower-level CsvCallbackHandler
directly requires to implement getFieldCount
(previously implemented in the RecordWrapper) and getRecordType
methods.
Null-free refactoring in CollectingStatusListener
Section titled “Null-free refactoring in CollectingStatusListener”The method getThrowable
of CollectingStatusListener
no longer returns null
when no exception has occurred. Instead, it returns an Optional<Throwable>
. This aligns with the null-free design of FastCSV.
Removed deprecated code
Section titled “Removed deprecated code”In FastCSV 4.0 some deprecated code was removed.
Callback handler instantiation
Section titled “Callback handler instantiation”The constructors of CsvRecordHandler
, NamedCsvRecordHandler
, and StringArrayHandler
have been deprecated in 3.6.0 and now have been removed in 4.0.0.
Just use the builder methods instead:
NamedCsvRecordHandler defaultHandler = new NamedCsvRecordHandler(); NamedCsvRecordHandler.of();
NamedCsvRecordHandler predefinedHeaderWithTrim = new NamedCsvRecordHandler(FieldModifiers.TRIM, "foo", "bar"); NamedCsvRecordHandler.builder() .fieldModifier(FieldModifiers.TRIM) .header("foo", "bar") .build();
// ... or functional-styleNamedCsvRecordHandler predefinedHeaderWithTrim = new NamedCsvRecordHandler(FieldModifiers.TRIM, "foo", "bar"); NamedCsvRecordHandler.of(builder -> builder .fieldModifier(FieldModifiers.TRIM) .header("foo", "bar") );
This change was necessary because callback handlers now have more configuration options, making constructor initialization impractical.
Configuring limits
Section titled “Configuring limits”In FastCSV 3.2.0, the default limits for the maximum number of fields per record and the maximum field size were made configurable
via system properties fastcsv.max.field.count
and fastcsv.max.field.size
.
In version 3.6.0 these properties were deprecated in favor of a more flexible configuration via the builder methods of CsvReader
and CallbackHandler
.
In version 4.0.0 those deprecated properties were removed.
// Set the maximum number of fields per recordSystem.setProperty("fastcsv.max.field.count", "16384");CsvRecordHandler handler = CsvRecordHandler.builder() .maxFields(16_384) .maxFieldSize(16_777_216) .build();
// Set the maximum buffer size and maximum field sizeSystem.setProperty("fastcsv.max.field.size", "16777216");CsvReader csvReader = CsvReader.builder() .maxBufferSize(16_777_216) .build(handler, file);
SimpleFieldModifier
Section titled “SimpleFieldModifier”The SimpleFieldModifier
class has been deprecated in 3.7.0 and removed in 4.0.0.
Use FieldModifier.modify
instead, which has moved from FieldModifiers
to the FieldModifier
interface.
FieldModifier normalizeWhitespaces = (SimpleFieldModifier) field -> field.replaceAll("\\s", " "); FieldModifier.modify(field -> field.replaceAll("\\s", " "));
Upper and lower case field modifiers
Section titled “Upper and lower case field modifiers”The edge-case methods FieldModifiers.lower(Locale)
and FieldModifiers.upper(Locale)
were deprecated in 3.7.0 and removed in 4.0.0.
Use FieldModifier.modify
instead, which has moved from FieldModifiers
to the FieldModifier
interface.
FieldModifier toLowerFieldModifier = FieldModifiers.lower(Locale.ENGLISH); FieldModifier.modify(field -> field.toLowerCase(Locale.ENGLISH));
FieldModifier toUpperFieldModifier = FieldModifiers.upper(Locale.ENGLISH); FieldModifier.modify(field -> field.toUpperCase(Locale.ENGLISH));