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()
.writeValues(strW);
seqW.write(new Object[] {"foo", 13, true });
seqW.write(Arrays.asList("bar", 28, false ));
seqW.close();
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)
.writeValueAsString(value);
// foo;13;true
// bar;28;false
try (StringWriter strW = new StringWriter()) {
SequenceWriter seqW = CSV_MAPPER.writer()
.writeValues(strW);
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()
.addColumn("name")
.addColumn("age")
.addColumn("validated")
.build();
// 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)
.writeValues(strW);
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)
.withHeader();
// 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)
.withHeader();
try (StringWriter strW = new StringWriter()) {
SequenceWriter seqW = MAPPER.writer(schema)
.writeValues(strW);
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()
.enable(CsvGenerator.Feature.ALWAYS_QUOTE_EMPTY_STRINGS)
.build();
  • 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
@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