Header validation
When reading CSV files with a header (using NamedCsvRecordHandler), you may want to assert that the file
actually comes with the header you expect – and fail fast (before processing any records) if it doesn’t.
FastCSV allows you to do this by configuring a HeaderValidator. The validator is called once the header is
determined – either from the first record or from a predefined header. If the validation fails,
a CsvParseException is thrown. Input without any header (e.g., an empty file) yields no records
and is not validated.
Two standard validators are provided:
HeaderValidator.containsExactly(...): the header must consist of exactly the given fields, in the given order.HeaderValidator.containsAtLeast(...): the header must contain at least the given fields, in any order.
For anything beyond that (e.g., case-insensitive comparison or pattern matching), you can implement custom validation logic using a lambda expression.
Example
Section titled “Example”In the following example, the header is validated while reading a CSV file.
import de.siegmar.fastcsv.reader.CsvParseException;import de.siegmar.fastcsv.reader.CsvReader;import de.siegmar.fastcsv.reader.HeaderValidator;import de.siegmar.fastcsv.reader.NamedCsvRecordHandler;
/// Example for reading CSV data while validating the header.////// FastCSV supports Java 17 and later, but this code uses Java 25/// for brevity, leveraging newer language features.void main() { final String data = """ header1,header2 foo,bar """;
IO.println("Require an exact header (order-sensitive):"); final NamedCsvRecordHandler exactHandler = NamedCsvRecordHandler.of(builder -> builder.headerValidator(HeaderValidator.containsExactly("header1", "header2")) ); CsvReader.builder().build(exactHandler, data) .forEach(rec -> IO.println(rec.getField("header2")));
IO.println("Require certain fields (in any order; additional fields are allowed):"); final NamedCsvRecordHandler atLeastHandler = NamedCsvRecordHandler.of(builder -> builder.headerValidator(HeaderValidator.containsAtLeast("header2")) ); CsvReader.builder().build(atLeastHandler, data) .forEach(rec -> IO.println(rec.getField("header2")));
IO.println("Custom validation logic:"); final NamedCsvRecordHandler customHandler = NamedCsvRecordHandler.of(builder -> builder.headerValidator(header -> { if (header.size() != 2) { throw new CsvParseException("Expected 2 header fields, but found " + header.size()); } }) ); CsvReader.builder().build(customHandler, data) .forEach(rec -> IO.println(rec.getField("header2")));
IO.println("A failed validation aborts reading with a CsvParseException:"); final NamedCsvRecordHandler failingHandler = NamedCsvRecordHandler.of(builder -> builder.headerValidator(HeaderValidator.containsExactly("headerA", "headerB")) ); try { CsvReader.builder().build(failingHandler, data) .forEach(rec -> IO.println(rec.getField("header2"))); } catch (final CsvParseException e) { IO.println(e.getMessage()); }}You also find this source code example in theFastCSV GitHub repository.