Reading CSV with Jackson

“How to use jackson-dataformat-csv”

First there was just JSON

Why and how is CSV different from JSON

  • CSV is tabular format, with columns and rows; JSON is Tree-structured with more flexible structure at format level.
  • CSV is positional: columns are identified by index, whereas JSON mostly uses named properties (although JSON does have positional Arrays too). It may or may not contain logical names for columns (more on this bit later)

Simplest, “untyped” reading of CSV as List<List<String>>

1,2,true
2,9,false
-13,0,true
final String CSV_DOC = "1,2,true\n2,9,false\n-13,0,true\n";
final CsvMapper mapper = new CsvMapper();
MappingIterator<List<String>> it = mapper
.readerForListOf(String.class)
.with(CsvParser.Feature.WRAP_AS_ARRAY) // !!! IMPORTANT
.readValues(CSV_DOC);
// If we want them all we use:
List<List<String>> all = it.readAll();
// or if not, we would instead:
while (it.hasNextValue()) {
List<String> row = it.nextValue();
// process
}
  • We need to construct CsvMapper instead of general ObjectMapper (or JsonMapper) to make sure we read/write CSV encoded content
  • We use convenience method readerForListOf() to get ObjectReader for reading List<String> values
  • We MUST enable one specific CSV feature to force individual rows to be exposed as equivalent to JSON Arrays — this is only needed when we do not use CsvSchema (explained in following sections)
  • We have at least 2 ways to read contents: MappingIterator gives rows one by one, but also has convenience method readAll() for “just read them all” slurping of content

Almost as simple: reading contents as Maps, POJOs

CsvSchema schema = CsvSchema.builder()
.addColumn("x")
.addColumn("y")
.addColumn("visible")
.build();
MappingIterator<Map<String, String>> it = mapper
.readerForMapOf(String.class)
// NOTE: no wrapping needed
.with(schema)
.readValues(CSV_DOC);
Map<String, String> row = it.nextValue();
assertEquals("1", map.get("x"));
assertEquals("2", map.get("y"));
assertEquals("true", map.get("visible"));
// CSV module defaults to alphabetic ordering so this is optional:
@JsonPropertyOrder({ "x", "y", "visible" })
public class Point {
public int x, y;
public boolean visible;
}
MappingIterator<Point> it = mapper
.readerFor(Point.class)
.with(schema)
.readValues(CSV_DOC);
while (it.hasNextValue()) {
Point p = it.nextValue();
int x = p.x;
// do something!
}
// or, you could alternative slurp 'em all:
List<Point> points = it.readAll();

With a little help from The Header

CsvSchema pointSchema = mapper.schemaFor(Point.class);
x,y,visible
1,2,true
2,9,false
-13,0,true
CsvSchema headerSchema = CsvSchema.emptySchema().withHeader();
String CSV_WITH_HEADER = ...; // see example above
MappingIterator<Map<String, String>> it = mapper
.readerFor(Point.class)
.with(headerSchema)
.readValues(CSV_WITH_HEADER);
// and read same as before

And That’s All For Now!

  1. Configuring CsvMapper with CsvParser.Feature — there are about a dozen settings for configuring aspects of possible comments, what to do with “extra” columns, whether to allow (and skip) empty lines and so on
  2. Configuring reading of specific documents by constructing and configuring CsvSchema instance used for reading: there are a few settings related to separator used (only defaults to Comma, can be changed), escape and/or quote character (if any) used and so forth

--

--

Open Source developer, most known for Jackson data processor (nee “JSON library”), author of many, many other OSS libraries for Java, from ClassMate to Woodstox

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
@cowtowncoder

Open Source developer, most known for Jackson data processor (nee “JSON library”), author of many, many other OSS libraries for Java, from ClassMate to Woodstox