diff --git a/release-notes/CREDITS b/release-notes/CREDITS index 64821d1..a62d58e 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -45,3 +45,8 @@ sothmann@github) * Reported #83: Serializing List with null values leads to corrupt CSV (2.6.0) + +Jonathan Cheseaux (cheseaux@github) + +* Reported #90: Unexpected output with arrays starting with a null/empty element + (2.6.4) diff --git a/release-notes/VERSION b/release-notes/VERSION index 4c11ff4..d906ebf 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -4,6 +4,11 @@ Project: jackson-dataformat-csv === Releases === ------------------------------------------------------------------------ +2.6.4 (not yet released) + +#90: Unexpected output with arrays starting with a null/empty element + (reported by Jonathan C) + 2.6.3 (12-Oct-2015) #91: Ensure that "too many columns" is recoverable diff --git a/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java b/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java index df32bec..4fd3f1e 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java @@ -158,7 +158,19 @@ private Feature(boolean defaultState) { */ protected int _arraySeparator = -1; + /** + * Accumulated contents of an array cell, if any + */ protected StringBuilder _arrayContents; + + /** + * Additional counter that indicates number of value entries in the + * array. Needed because `null` entries do not add content, but need + * to be separated by array cell separator + * + * @since 2.7 + */ + protected int _arrayElements; /* /********************************************************** @@ -424,6 +436,7 @@ public final void writeStartArray() throws IOException } else { _arrayContents.setLength(0); } + _arrayElements = 0; } } else if (_arraySeparator >= 0) { // also: no nested arrays, yet @@ -863,16 +876,18 @@ protected void _handleFirstLine() throws IOException } protected void _addToArray(String value) { - if (_arrayContents.length() > 0) { + if (_arrayElements > 0) { _arrayContents.append((char) _arraySeparator); } + ++_arrayElements; _arrayContents.append(value); } protected void _addToArray(char[] value) { - if (_arrayContents.length() > 0) { + if (_arrayElements > 0) { _arrayContents.append((char) _arraySeparator); } + ++_arrayElements; _arrayContents.append(value); } } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/TestParserWorkarounds.java b/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/TestParserWorkarounds.java index d05b1b9..2249c04 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/TestParserWorkarounds.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/TestParserWorkarounds.java @@ -27,7 +27,6 @@ public void testIgnoringOptionalTrailing() throws Exception .addColumn("second") .build(); - @SuppressWarnings("resource") MappingIterator> it = mapper.reader(schema).forType(Map.class).readValues( "a,b\nc,d,\ne,f, \nfoo,bar,x\n"); assertTrue(it.hasNext()); @@ -59,6 +58,7 @@ public void testIgnoringOptionalTrailing() throws Exception } catch (JsonParseException e) { verifyException(e, "Too many entries"); } + it.close(); } // also ensure [databind-csv#1] also works appropriately for failing case @@ -70,7 +70,6 @@ public void testOptionalTrailFailing() throws Exception .addColumn("second") .build(); - @SuppressWarnings("resource") MappingIterator> it = mapper.reader(schema).forType(Map.class).readValues( "a,b,\nc,d,,,\n"); assertTrue(it.hasNext()); @@ -88,5 +87,6 @@ public void testOptionalTrailFailing() throws Exception } catch (JsonProcessingException e) { verifyException(e, "Too many entries: expected at most 2"); } + it.close(); } } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/ArrayWriteTest.java b/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/ArrayWriteTest.java index bc13aba..4953636 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/ArrayWriteTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/ArrayWriteTest.java @@ -4,7 +4,8 @@ import com.fasterxml.jackson.dataformat.csv.*; -// for [dataformat-csv#57] +// Tests for verifying that it is possible to write "simple" arrays +// (ones with scalar serializations) as CSV public class ArrayWriteTest extends ModuleTestBase { @JsonPropertyOrder({"id", "values", "extra"}) @@ -19,6 +20,16 @@ public ValueEntry(String id, String extra, int... v) { } } + @JsonPropertyOrder({"a", "b", "c"}) + static class Pojo90 { + + public String[] a = new String[]{"", "foo"}; + + public String[] b = new String[]{null, "bar"}; + + public String[] c = new String[]{"baz",null}; + } + /* /********************************************************************** /* Test methods @@ -49,4 +60,17 @@ public void testSeparatorOverride() throws Exception // gets quoted due to white space assertEquals("foo,\"1 2 3\",stuff", csv); } + + public void testArraysWithNulls() throws Exception + { + Pojo90 value = new Pojo90(); + CsvMapper mapper = mapperForCsv(); + String csvContent = mapper.writer(mapper.schemaFor(Pojo90.class) + .withHeader()) + .writeValueAsString(value); + String[] lines = csvContent.split("\\n"); + assertEquals(2, lines.length); + assertEquals("a,b,c", lines[0]); + assertEquals(";foo,;bar,baz;", lines[1]); + } }