Writing CSV with Jackson

“How to use jackson-dataformat-csv, part 2”

Now that we know how to read CSV with Jackson…

  • While Jackson started as a JSON library, it has expanded via so-called “format modules” to support a dozen other formats, including Comma-Separated Values (CSV)
  • CSV is quite different from JSON in multiple ways: it is tabular, positional text format. This means that there are some additional constructs to use when reading and/or writing CSV, compared to JSON handling. Most of Jackson API is the same, however; the main addition is concept of CsvSchema, used to bind positional nature of values in CSV documents (columns) to JSON-style named properties.

Simple, “untyped” writing of CSV data

final CsvMapper CSV_MAPPER = new CsvMapper();
final Object[] value = new Object[] { // could use List as well
new Object[] { "foo", 13, true },
new Object[] { "bar", 28, false };
String csv = CSV_MAPPER.writeValueAsString(value);
// foo,13,true
// bar,28,false
try (StringWriter strW = new StringWriter()) {
SequenceWriter seqW = MAPPER.writer()
seqW.write(new Object[] {"foo", 13, true });
seqW.write(Arrays.asList("bar", 28, false ));
String csv = strW.toString();
// same as above
CsvSchema schema = CsvSchema.emptySchema()
.withQuoteChar('\'') // instead of double-quote
.withColumnSeparator(';') // instead of comma
.withLineSeparator("\r\n") // instead of \n
String csv = CSV_MAPPER.writer(schema)
// foo;13;true
// bar;28;false
try (StringWriter strW = new StringWriter()) {
SequenceWriter seqW = CSV_MAPPER.writer()
seqW.write(Arrays.asList("name", "age", "validated"));
seqW.write(Arrays.asList(bar", 28, false ));
// ... and so on

Con POJOs, por favor

// manually create by adding columns in order:
CsvSchema schema = CsvSchema.builder()
// or read from POJO
@JsonPropertyOrder({ "name", "age", "validated" }) // important!
// ^^^ without annotation properties ordered alphabetically
public class Person {
public String name;
public int age;
public boolean validated;
public Person(String n, int a, boolean v) {
name = n;
age = a;
validated = v;
CsvSchema altSchema = CSV_MAPPER.schemaFor(Person.class)
try (StringWriter strW = new StringWriter()) {
// NOTE! Below will introspect and apply schema!
SequenceWriter seqW = MAPPER.writerWithSchemaFor(Person.class)
seqW.write(new Person("Bob", 37, false))
seqW.write(new Person("Jeff", 28, true))
// Bob,37,false
// Jeff,28,true
final CsvSchema schema = CSV_MAPPER.schemaFor(Person.class)
// and with write sequence from above, we'd get:
// name,age,validated
// Bob,37,false
// Jeff,28,true

Special output: Arrays/Lists

@JsonPropertyOrder({ "name", "value", "tags" })
public class Metric {
public String name;
public double value;
public Collection<String> tags;
final CsvSchema schema = CSV_MAPPER.schemaFor(Metric.class)
try (StringWriter strW = new StringWriter()) {
SequenceWriter seqW = MAPPER.writer(schema)
seqW.write(new Metric("latency", 0.2, Arrays.asList( "http", "rest")));
// name,value,tags
// latency,0.2,http;rest

Anything else? Oh yes, configuration

  1. Use specifically configured CsvSchema
  2. Configure CsvMapper with one or more CsvGenerator.Features

Configuration using CsvSchema

  • Column value separator (default: comma); may want to change to, say, semicolon — change with schema.withColumnSeparator()
  • Line separator (default: linefeed (\n)) — change with schema.withLineSeparator()
  • Escape character, if any (default: none) — change with schema.withEscapeChar()
  • Whether comments (line starting with #) are allowed (and if so, skipped/ignored; default: not allowed) — enable with schema.withComments()
  • “Null value” to use when Java null written (default value: "" (empty String)) — change with schema.withNullValue()
  • Array element separate (default: ; (semicolon)) to use in case where a column is expected to contain Array value (NOTE: expectation of Array value must come from POJO property target, NOT CsvSchema column definition) — change with schema.withArrayElementSeparator()
  • “Any property” name (default: none): in case CSV row has more entries than defined columns (as specified by header row or CsvSchema), additional entries may be exposed as “any” properties with a fixed name. This is usually used in conjunction with Jackson @JsonAnySetter annotation, to collect set of extra information — change with schema.withAnyPropertyName()

Configuration using CsvGenerator.Feature’s

CsvMapper mapper = CsvMapper.builder()
  • STRICT_CHECK_FOR_QUOTING (default: false): whether the whole output value is checked to determine if quoting (surrounding value in double quotes) is needed — if disabled, only sampling of first N characters is used, and longer values are automatically quoted without checking
  • OMIT_MISSING_TAIL_COLUMNS (default: false): if last column values are not written during serialization, is it ok to just ignore them (and separators between values), or should placeholders (empty Strings) be written
  • ALWAYS_QUOTE_STRINGS (default: false): Should String values always be quoted, regardless of possible need
  • ALWAYS_QUOTE_EMPTY_STRINGS (default: false): Should empty String values always be quoted or not?
  • ESCAPE_QUOTE_CHAR_WITH_ESCAPE_CHAR (default: false) Should quote character itself be escaped using “escape character” (true) or by writing quote character twice (false)
  • ESCAPE_CONTROL_CHARS_WITH_ESCAPE_CHAR (default: false) Should Unicode/Ascii control characters (including linefeed/carriage return) be escaped (prepended with escape character, \ by default) (true) or be included as-is (false)

And That’s All, Folks!



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

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