The matrix-csv module reads and writes CSV-style tabular data through a fluent API, typed options objects, and the generic Matrix SPI. It supports standard CSV plus .tsv and .tab files, and uses Apache Commons CSV internally while keeping the recommended API surface Matrix-specific.
Use the Matrix BOM when you depend on multiple Matrix modules:
implementation platform("se.alipsa.matrix:matrix-bom:2.5.1")
implementation "se.alipsa.matrix:matrix-core"
implementation "se.alipsa.matrix:matrix-csv"<project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>se.alipsa.matrix</groupId>
<artifactId>matrix-bom</artifactId>
<version>2.5.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>se.alipsa.matrix</groupId>
<artifactId>matrix-core</artifactId>
</dependency>
<dependency>
<groupId>se.alipsa.matrix</groupId>
<artifactId>matrix-csv</artifactId>
</dependency>
</dependencies>
</project>The fluent API is the primary way to use matrix-csv.
import se.alipsa.matrix.core.Matrix
import se.alipsa.matrix.csv.CsvReader
Matrix basic = CsvReader.read().from(new File("basic.csv"))
Matrix semicolon = CsvReader.read()
.delimiter(';')
.from(new File("sales.csv"))
Matrix fromString = CsvReader.read()
.fromString("name,age\nAlice,30\nBob,25\n")Matrix matrix = CsvReader.read()
.firstRowAsHeader(false)
.fromString("1,Alice,100.00\n2,Bob,200.00\n")
assert ['c0', 'c1', 'c2'] == matrix.columnNames()If you already know both headers and types, use columns(...):
Matrix typed = CsvReader.read()
.columns(id: Integer, name: String, amount: BigDecimal)
.fromString("1,Alice,100.00\n2,Bob,200.00\n")import java.time.LocalDate
Matrix sales = CsvReader.read()
.types(Integer, String, LocalDate, BigDecimal)
.dateTimeFormat('yyyy-MM-dd')
.fromString("""id,name,date,amount
1,Alice,2025-01-15,100.50
2,Bob,2025-01-20,200.75
""")For locale-specific numbers, add numberFormat(...).
Matrix excel = CsvReader.read()
.excel()
.from(new File("report.csv"))
Matrix tsv = CsvReader.read()
.tsv()
.from(new File("report.tsv"))excel() applies Apache Excel semantics, including CRLF records and more literal read handling (trim(false), ignoreEmptyLines(false), ignoreSurroundingSpaces(false)).
import se.alipsa.matrix.csv.CsvWriter
CsvWriter.write(sales).to(new File("sales-copy.csv"))
String csv = CsvWriter.write(sales).asString()
CsvWriter.write(sales)
.delimiter(';')
.to(new File("sales-semicolon.csv"))
CsvWriter.write(sales)
.withHeader(false)
.to(new File("sales-no-header.csv"))CsvWriter.write(sales).excel().to(new File("sales-excel.csv"))
CsvWriter.write(sales).tsv().to(new File("sales.tsv"))excel() writes CRLF-separated output and quotes all non-null values.
Use typed options when you want an explicit configuration object instead of chained fluent calls.
import org.apache.commons.csv.DuplicateHeaderMode
import se.alipsa.matrix.csv.CsvReadOptions
import se.alipsa.matrix.csv.CsvReader
CsvReadOptions readOptions = new CsvReadOptions()
.delimiter(';')
.charset('ISO-8859-1')
.tableName('sales')
.types([Integer, String, BigDecimal])
.duplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL)
Matrix sales = CsvReader.read(new File("sales.csv"), readOptions)Read-side source semantics:
- charset affects
File,Path,URL, andInputStream - charset has no effect for
ReaderorString tableName(...)overrides source-derived matrix names.tsvand.tabauto-detect tab delimiters unless the delimiter was explicitly configured
import se.alipsa.matrix.csv.CsvWriteOptions
import se.alipsa.matrix.csv.CsvWriter
CsvWriteOptions writeOptions = new CsvWriteOptions()
.delimiter(';')
.escape('\\')
.nullString('NULL')
.recordSeparator('\r\n')
CsvWriter.write(sales, new File("sales.csv"), writeOptions)
String csv = CsvWriter.writeString(sales, writeOptions)The typed write contract only includes options with observable write behavior:
- delimiter
- quote
- escape
- withHeader
- charset
- recordSeparator
- nullString
Parse-only keys such as trim, ignoreEmptyLines, ignoreSurroundingSpaces, and commentMarker are rejected when used as write options.
The typed options classes can normalize SPI-style maps:
import se.alipsa.matrix.csv.CsvReadOptions
import se.alipsa.matrix.csv.CsvWriteOptions
CsvReadOptions readOptions = CsvReadOptions.fromMap([
delimiter : ';',
duplicateHeaderMode: 'ALLOW_ALL'
])
CsvWriteOptions writeOptions = CsvWriteOptions.fromMap([
delimiter : ';',
escape : '\\',
nullString : 'NULL'
])This keeps direct typed usage and SPI usage aligned.
If matrix-csv is on the classpath, Matrix automatically discovers the CSV provider for .csv, .tsv, and .tab.
import se.alipsa.matrix.core.Matrix
Matrix csv = Matrix.read(new File("basic.csv"))
Matrix renamed = Matrix.read([tableName: 'sales'], new File("sales.csv"))
Matrix tsv = Matrix.read(new File("report.tsv"))
csv.write(new File("copy.csv"))
csv.write([delimiter: ';', nullString: 'NULL'], new File("copy.csv"))Runtime option discovery is available through:
import se.alipsa.matrix.csv.CsvReadOptions
import se.alipsa.matrix.csv.CsvWriteOptions
import se.alipsa.matrix.core.Matrix
println Matrix.listReadOptions('csv')
println Matrix.listWriteOptions('csv')
println CsvReadOptions.describe()
println CsvWriteOptions.describe()The old overloads that accept Apache Commons CSVFormat directly still exist for compatibility, but they are deprecated. Prefer one of these instead:
- the fluent
CsvReader/CsvWriterAPI - the typed
CsvReadOptions/CsvWriteOptionsAPI - the generic
Matrix.read(...)/matrix.write(...)SPI
Use the fluent API by default, reach for typed options when you want explicit reusable configuration, and use the generic Matrix SPI when you want extension-based file handling. The three paths share the same behavior and option normalization.
Go to previous section | Go to next section | Back to outline