BoxLang 🚀 A New JVM Dynamic Language Learn More...
|:------------------------------------------------------: |
| ⚡︎ B o x L a n g ⚡︎
| Dynamic : Modular : Productive |
| :----------------------------:  |
Copyright Since 2023 by Ortus Solutions, Corp
www.boxlang.io | www.ortussolutions.com
The BoxLang CSV module provides a comprehensive and modern API for working with CSV (Comma-Separated Values) files in BoxLang. Built on top of Apache Commons CSV, this module offers both traditional BIF-style functions and a powerful fluent API for creating, reading, parsing, and manipulating CSV files.
process()
filter() map()
Install the module using CommandBox:
box install bx-csv
Here's a taste of what you can do with the fluent API:
// Create and write a CSV file with fluent API (recommended)
CSV()
    .setHeaders( "Name", "Email", "Age" )
    .addRow( [ "John Doe", "[email protected]", 30 ] )
    .addRow( [ "Jane Smith", "[email protected]", 25 ] )
    .save( "contacts.csv" );
// Load and parse an existing CSV file
data = CSV( "data.csv" ).toArray();
// Stream process a large file
CSV().process( "huge-file.csv", ( row ) => {
    // Process each row without loading entire file into memory
    println( "Name: " & row[1] );
} );
💡 Best Practice: We recommend using the fluent
CSV()API directly over the individual BIFs for a more modern, readable, and maintainable codebase.
There are multiple ways to create CSV files: using the fluent API (recommended), traditional BIFs, or static constructors.
// Create a new empty CSV file
csv = CSV();
// Create from a path
csv = CSV( "data.csv" );
// Create with custom delimiter
csv = CSV( path="data.csv", delimiter="|" );
// Create with configuration
csv = CSV()
    .delimiter( '|' )
    .headers( true )
    .trim( true );
// Create from JSON
jsonData = '[{"name":"John","age":30},{"name":"Jane","age":25}]';
csv = CSVFile.fromJson( jsonData );
// Create from Array of Structs
data = [
    { "name": "John", "age": 30 },
    { "name": "Jane", "age": 25 }
];
csv = CSVFile.fromArray( data );
// Create from Query
qData = queryExecute( "SELECT name, email FROM users" );
csv = CSVFile.fromQuery( qData );
// Create empty
csv = CSVFile.empty();
// Fluent API - build CSV data
CSV()
    .setHeaders( "Name", "Email", "Age" )
    .addRow( [ "John Doe", "[email protected]", 30 ] )
    .addRow( [ "Jane Smith", "[email protected]", 25 ] )
    .addRow( [ "Bob Johnson", "[email protected]", 35 ] )
    .save( "contacts.csv" );
// Set specific rows (1-based indexing)
CSV()
    .setRowData( 1, [ "Product", "Price", "Quantity" ] )
    .setRowData( 2, [ "Widget A", 10.99, 100 ] )
    .setRowData( 3, [ "Widget B", 15.99, 50 ] )
    .save( "products.csv" );
// Load entire file into memory
csv = CSV( "data.csv" );
// Or load explicitly
csv = CSV().load( "data.csv" );
// Get all data as array
allData = csv.toArray();
// Get headers
headers = csv.getHeaders();
// Get row count
rowCount = csv.getRowCount();
// Get specific row (1-based)
row2 = csv.getRowData( 2 );
// Parse CSV content from a string
csvContent = "Name,Age,City
John,30,NYC
Jane,25,LA";
csv = CSV().loadFromString( csvContent );
data = csv.toArray();
// Load with custom delimiter and options
csv = CSV()
    .delimiter( '|' )
    .trim( true )
    .skipHeaderRecord( false )
    .load( "data.txt" );
// Save to file
csv = CSV()
    .setHeaders( "Col1", "Col2", "Col3" )
    .addRow( [ "A", "B", "C" ] )
    .save( "output.csv" );
// Save with overwrite
csv.overwrite( true ).save( "existing.csv" );
// Save to specific path
csv.setPath( "reports/data.csv" ).save();
The CSVFile class provides convenient methods for exporting CSV data to various formats and importing data from those formats.
// Create CSV and export to JSON
csv = CSV()
    .setHeaders( "Name", "Age", "City" )
    .addRow( [ "John", 30, "New York" ] )
    .addRow( [ "Jane", 25, "Los Angeles" ] );
jsonString = csv.toJson();
// Returns: [{"Name":"John","Age":"30","City":"New York"},{"Name":"Jane","Age":"25","City":"Los Angeles"}]
// Pretty-print JSON
jsonPretty = csv.toJson( true );
// Convert CSV to BoxLang Query object
csv = CSV()
    .setHeaders( "Product", "Price", "Quantity" )
    .addRow( [ "Widget A", "10.99", "100" ] )
    .addRow( [ "Widget B", "15.99", "50" ] );
query = csv.toQuery();
// Query object with VARCHAR columns
// Export as array of arrays
csv = CSV( "data.csv" );
arrayData = csv.toArray();
// Convert to CSV string
csv = CSV()
    .setHeaders( "A", "B", "C" )
    .addRow( [ "1", "2", "3" ] );
csvString = csv.toString();
// Returns: "A,B,C\n1,2,3\n"
// Create CSV from JSON string
jsonData = '[{"Name":"Alice","Score":95},{"Name":"Bob","Score":87}]';
csv = CSVFile.fromJson( jsonData );
csv.save( "scores.csv" );
// Create CSV from array of structs
data = [
    { "Product": "Widget A", "Price": 10.99, "InStock": true },
    { "Product": "Widget B", "Price": 15.99, "InStock": false }
];
csv = CSVFile.fromArray( data );
csv.save( "products.csv" );
// Create CSV from query results
qData = queryExecute( "SELECT name, email, status FROM users" );
csv = CSVFile.fromQuery( qData );
csv.save( "users-export.csv" );
// Export to JSON, modify, and import back
original = CSV()
    .setHeaders( "Name", "Email" )
    .addRow( [ "John", "[email protected]" ] );
json = original.toJson();
// Send JSON to API, save to database, etc.
// ...
// Later, import back
imported = CSVFile.fromJson( json );
imported.save( "restored.csv" );
// CSV with headers enabled (default)
csv = CSV()
    .headers( true )
    .setHeaders( "Name", "Age", "Email" )
    .addRow( [ "John", 30, "[email protected]" ] );
// Get headers
headers = csv.getHeaders();
// Returns: ["Name", "Age", "Email"]
// CSV without headers
csv = CSV()
    .headers( false )
    .addRow( [ "John", "30", "[email protected]" ] )
    .addRow( [ "Jane", "25", "[email protected]" ] );
// Pipe-delimited file
csv = CSV()
    .delimiter( '|' )
    .setHeaders( "Name", "Age", "City" )
    .addRow( [ "John", 30, "New York" ] )
    .save( "data.txt" );
// Tab-delimited (TSV)
csv = CSV()
    .delimiter( '\t' )
    .load( "data.tsv" );
// Custom quote character
csv = CSV()
    .quote( '\'' )
    .escape( '\'' )
    .load( "data.csv" );
// Filter rows during load (only include adults)
csv = CSV()
    .filter( ( row ) => {
        return row[2] >= 18; // Age column
    } )
    .load( "people.csv" );
// Transform rows during load (uppercase names)
csv = CSV()
    .map( ( row ) => {
        row[0] = row[0].toUpperCase();
        return row;
    } )
    .load( "data.csv" );
// Combine filter and map
csv = CSV()
    .filter( ( row ) => row[1] != "" ) // Skip empty emails
    .map( ( row ) => {
        row[1] = row[1].trim().toLowerCase();
        return row;
    } )
    .load( "contacts.csv" );
// Unix line endings
csv = CSV()
    .lineSeparator( "\n" )
    .save( "unix.csv" );
// Windows line endings
csv = CSV()
    .lineSeparator( "\r\n" )
    .save( "windows.csv" );
// Mac classic line endings
csv = CSV()
    .lineSeparator( "\r" )
    .save( "mac.csv" );
// Trim whitespace from values
csv = CSV()
    .trim( true )
    .load( "data.csv" );
// Ignore surrounding spaces (more comprehensive than trim)
csv = CSV()
    .ignoreSurroundingSpaces( true )
    .load( "data.csv" );
// Skip empty lines (default: true)
csv = CSV()
    .ignoreEmptyLines( true )
    .load( "data.csv" );
// Support comment lines
csv = CSV()
    .commentMarker( '#' )
    .load( "data.csv" );
// Lines starting with # will be ignored
// Add header comments when writing
csv = CSV()
    .commentMarker( '#' )
    .headerComments( "Generated on " & now(), "Author: System" )
    .setHeaders( "Col1", "Col2" )
    .addRow( [ "A", "B" ] )
    .save( "data.csv" );
// Represent null values as "NULL"
csv = CSV()
    .nullString( "NULL" )
    .addRow( [ "John", null, "NYC" ] )
    .save( "data.csv" );
// Output: John,NULL,NYC
// Read null strings back
csv = CSV()
    .nullString( "NULL" )
    .load( "data.csv" );
// Quote all values
csv = CSV()
    .quoteMode( "ALL" )
    .save( "quoted.csv" );
// Quote only when necessary (default)
csv = CSV()
    .quoteMode( "MINIMAL" )
    .save( "minimal.csv" );
// Quote non-numeric values
csv = CSV()
    .quoteMode( "NON_NUMERIC" )
    .save( "numbers.csv" );
// Never quote
csv = CSV()
    .quoteMode( "NONE" )
    .save( "unquoted.csv" );
For large CSV files that don't fit in memory, use the
process() method to stream through rows:
// Process a large file without loading into memory
CSV().process( "huge-file.csv", ( row ) => {
    // Process each row individually
    println( "Processing: " & row[1] );
    // Do something with the row (database insert, API call, etc.)
    queryExecute(
        "INSERT INTO table (col1, col2) VALUES (?, ?)",
        [ row[1], row[2] ]
    );
} );
// Process with pre-configured CSV settings
CSV()
    .delimiter( '|' )
    .trim( true )
    .filter( ( row ) => row[2] > 0 ) // Only process positive values
    .process( "data.txt", ( row ) => {
        // Process filtered rows
        processRow( row );
    } );
// Stream with path set beforehand
CSV()
    .setPath( "large-file.csv" )
    .process( ( row ) => {
        // Process each row
        println( row[0] & ": " & row[1] );
    } );
// Query database and export to CSV
qData = queryExecute( "
    SELECT
        customer_name,
        order_date,
        product_name,
        quantity,
        unit_price,
        (quantity * unit_price) AS total
    FROM orders
    WHERE order_date >= ?
", [ dateAdd( "m", -1, now() ) ] );
// Convert to CSV with formatting
CSVFile.fromQuery( qData )
    .delimiter( ',' )
    .quoteMode( "MINIMAL" )
    .trim( true )
    .save( "reports/monthly-orders.csv" );
// Load existing CSV, transform data, save to new file
CSV( "input/raw-data.csv" )
    .filter( ( row ) => {
        // Only include rows with valid email
        return row[2].find( "@" ) > 0;
    } )
    .map( ( row ) => {
        // Normalize data
        row[0] = row[0].trim().toUpperCase(); // Name
        row[1] = row[1].trim(); // Phone
        row[2] = row[2].trim().toLowerCase(); // Email
        return row;
    } )
    .trim( true )
    .save( "output/cleaned-data.csv" );
// Combine multiple CSV files into one
combined = CSV().setHeaders( "Name", "Email", "Source" );
// Process each file and add to combined
[ "file1.csv", "file2.csv", "file3.csv" ].each( ( file ) => {
    CSV().process( file, ( row ) => {
        // Skip header row if present
        if ( row[1] != "Name" ) {
            // Add source file name
            combined.addRow( [ row[1], row[2], file ] );
        }
    } );
} );
combined.save( "combined-output.csv" );
// CSV to JSON
CSV( "data.csv" ).toJson( true ); // Pretty-printed
// JSON to CSV
CSVFile.fromJson( myJsonString ).save( "output.csv" );
// Query to CSV
CSVFile.fromQuery( myQuery ).save( "export.csv" );
// CSV to Query
query = CSV( "data.csv" ).toQuery();
// Allow duplicate column names
csv = CSV()
    .allowDuplicateHeaderNames( true )
    .load( "data-with-dupes.csv" );
// Disallow duplicates (throws exception)
csv = CSV()
    .allowDuplicateHeaderNames( false )
    .load( "data.csv" );
// Skip first row as header (default: true)
csv = CSV()
    .skipHeaderRecord( true )
    .load( "data.csv" );
// First row becomes column names, not included in data
// Include header row in data (skipHeaderRecord=false)
csv = CSV()
    .skipHeaderRecord( false )
    .load( "data.csv" );
// First row is treated as data
// Auto-flush after each record (better reliability, slower)
csv = CSV()
    .autoFlush( true )
    .addRow( [ "A", "B", "C" ] )
    .save( "data.csv" );
// Add trailing delimiter at end of each row
csv = CSV()
    .trailingDelimiter( true )
    .save( "data.csv" );
// Output: A,B,C,
// For large in-memory datasets:
// - Use filter/map to reduce data size during load
// - Use streaming process() for files > 100MB
// For frequent writes:
// - Disable autoFlush for better performance
// - Build data in memory, save once
// For reading multiple files:
// - Use process() to avoid memory issues
// - Consider parallel processing in separate threads
// Example: Efficient processing
CSV()
    .ignoreEmptyLines( true )
    .trim( true )
    .filter( ( row ) => row[1] != "" ) // Skip invalid rows early
    .process( "large-file.csv", ( row ) => {
        // Minimal processing per row
        saveToDatabase( row );
    } );
Returns a fluent CSVFile object for creating or manipulating CSV files.
Parameters:
path (optional, string) - Path to load an existing file
or where the file will be saveddelimiter (optional, string) - Delimiter character
(default: ",")hasHeaders (optional, boolean) - Whether CSV has
headers (default: true)trim (optional, boolean) - Whether to trim whitespace
(default: true)ignoreEmptyLines (optional, boolean) - Whether to
ignore empty lines (default: true)skipHeaderRecord (optional, boolean) - Whether to skip
header record when parsing (default: true)Returns: CSVFile object
Example:
// Create new
csv = CSV();
// Load existing
csv = CSV( "data.csv" );
// Load with custom delimiter
csv = CSV( path="data.txt", delimiter="|" );
// Load with full configuration
csv = CSV(
    path="data.csv",
    delimiter=",",
    hasHeaders=true,
    trim=true,
    ignoreEmptyLines=true,
    skipHeaderRecord=true
);
The CSVFile class provides a fluent, chainable API for all CSV operations.
CSVFile.empty()
CSVFile.fromJson( jsonString )
jsonString (string) - JSON arrayCSVFile.fromJson('[{"name":"John","age":30}]')
CSVFile.fromArray( arrayOfStructs )
arrayOfStructs (array) - Array of structsCSVFile.fromQuery( query )
query (Query) - Query objectload( path )
path (string) - File pathcsv.load("data.csv")
loadFromString( csvContent )
csvContent (string) - CSV contentloadFromJson( jsonString )
jsonString (string) - JSON arrayloadFromQuery( query )
query (Query) - Query objectloadFromArray( arrayOfStructs )
arrayOfStructs (array) - Array of structssave( path, [overwrite] )
path (string) - File pathoverwrite (optional, boolean) - Whether to
overwrite existing filecsv.save("output.csv", true)
save()
delimiter( char )
char (character) - Delimiter (e.g., ',',
'|', '\t')csv.delimiter('|')
quote( char )
char (character) - Quote character
(default: '"')escape( char )
char (character) - Escape character
(default: '"')lineSeparator( string )
string (string) - Line separator (e.g.,
"\n", "\r\n")trim( boolean )
boolean (boolean) - Enable/disable trimmingheaders( boolean )
boolean (boolean) - Enable/disable headersskipHeaderRecord( boolean )
boolean (boolean) - Enable/disable skippingallowMissingColumnNames( boolean )
boolean (boolean) - Enable/disableignoreEmptyLines( boolean )
boolean (boolean) - Enable/disablenullString( string )
string (string) - Null representation
(e.g., "NULL", "N/A")commentMarker( char )
char (character) - Comment marker (e.g., '#')ignoreSurroundingSpaces( boolean )
boolean (boolean) - Enable/disablequoteMode( string )
string (string) - Quote mode:
"ALL", "MINIMAL", "NON_NUMERIC",
"NONE", "ALL_NON_NULL"csv.quoteMode("ALL")
allowDuplicateHeaderNames( boolean )
boolean (boolean) - Enable/disableautoFlush( boolean )
boolean (boolean) - Enable/disabletrailingDelimiter( boolean )
boolean (boolean) - Enable/disableheaderComments( ...comments )
comments (string...) - Variable arguments
of comment stringscsv.headerComments("File created on "
      & now(), "Author: System")
overwrite( boolean )
boolean (boolean) - Enable/disable overwritingsetPath( path )
path (string) - File pathsetHeaders( ...headers )
headers (string...) - Variable arguments
of header namescsv.setHeaders("Name", "Email", "Age")
setRowData( rowNumber, rowData )
rowNumber (number) - Row number (1-based)rowData (array) - Array of values for the rowcsv.setRowData(1, ["A", "B", "C"])
addRow( rowData )
rowData (array) - Array of values for the rowcsv.addRow(["John",
      "[email protected]", 30])
getRowData( rowNumber )
rowNumber (number) - Row number (1-based)getRowCount()
getColumnCount()
getHeaders()
clear()
filter( predicate )
predicate (function) - Function that
receives a row array and returns booleancsv.filter((row) => row[2] >= 18)
map( mapper )
mapper (function) - Function that
receives and returns a row arraycsv.map((row) => { row[0] =
      row[0].toUpperCase(); return row; })
process( path, consumer )
path (string) - Path to CSV fileconsumer (function) - Function that receives each
row arraycsv.process("large.csv", (row) => println(row[0]))
process( consumer )
consumer (function) - Function that
receives each row arraytoArray()
toJson( [pretty] )
pretty (optional, boolean) - Pretty-print
JSON (default: false)toQuery()
toString()
getPath()
getDelimiter()
hasHeaders()
getData()
"I am the way, and the truth, and the life; no one comes to the Father, but by me (JESUS)" - John 14:6
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
								$ 
									box install bx-csv