Skip to content

Commit e5beb4b

Browse files
authored
Fixes #10: missing null writes with Object[] (#585)
1 parent bc72beb commit e5beb4b

File tree

4 files changed

+41
-32
lines changed

4 files changed

+41
-32
lines changed

csv/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ private Feature(boolean defaultState) {
237237
*/
238238
protected boolean _skipValue;
239239

240+
/**
241+
* Flag set when a row has just been finished, used to distinguish between
242+
* null values within a row vs null rows.
243+
* Only relevant for Array-wrapped rows.
244+
*
245+
* @since 2.21
246+
*/
247+
protected boolean _justFinishedRow = false;
248+
240249
/**
241250
* Separator to use during writing of (simple) array value, to be encoded as a
242251
* single column value, if any.
@@ -562,6 +571,7 @@ public void close() throws IOException
562571
public final void writeStartArray() throws IOException
563572
{
564573
_verifyValueWrite("start an array");
574+
_justFinishedRow = false; // Clear flag when starting new array
565575
// Ok to create root-level array to contain Objects/Arrays, but
566576
// can not nest arrays in objects
567577
if (_tokenWriteContext.inObject()) {
@@ -636,6 +646,8 @@ public final void writeEndArray() throws IOException
636646
public final void writeStartObject() throws IOException
637647
{
638648
_verifyValueWrite("start an object");
649+
_justFinishedRow = false;
650+
639651
// No nesting for objects; can write Objects inside logical root-level arrays.
640652
// 14-Dec-2015, tatu: ... except, should be fine if we are ignoring the property
641653
if (_tokenWriteContext.inObject() ||
@@ -882,7 +894,12 @@ public void writeNull() throws IOException
882894
// just skip; can change, if so desired, to expose "root null" as empty rows, possibly
883895
// based on either schema property, or CsvGenerator.Feature.
884896
// Note: if nulls are to be written that way, would need to call `finishRow()` right after `writeNull()`
885-
if (!_tokenWriteContext.getParent().inRoot()) {
897+
// [dataformats-text#10]: When we have a schema and we haven't just finished a row,
898+
// it means we're inside an array-as-row (like Object[]), so null is a column value
899+
boolean writeNullValue = !_tokenWriteContext.getParent().inRoot()
900+
|| (_schema.size() > 0 && !_justFinishedRow);
901+
902+
if (writeNullValue) {
886903
// 26-Aug-2024, tatu: [dataformats-text#495] Decorations?
887904
if (_nextColumnDecorator != null) {
888905
String nvl = _nextColumnDecorator.decorateNull(this);
@@ -1132,6 +1149,7 @@ protected void finishRow() throws IOException
11321149
{
11331150
_writer.endRow();
11341151
_nextColumnByName = -1;
1152+
_justFinishedRow = true;
11351153
}
11361154

11371155
protected void _handleFirstLine() throws IOException
Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.dataformat.csv.tofix;
1+
package com.fasterxml.jackson.dataformat.csv.ser;
22

33
import java.io.StringWriter;
44

@@ -7,16 +7,12 @@
77
import com.fasterxml.jackson.databind.ObjectWriter;
88
import com.fasterxml.jackson.databind.SequenceWriter;
99
import com.fasterxml.jackson.dataformat.csv.*;
10-
import com.fasterxml.jackson.dataformat.csv.testutil.failure.JacksonTestFailureExpected;
1110

1211
import static org.junit.jupiter.api.Assertions.assertEquals;
1312

14-
public class MissingNullsOnObjectArrayWrite10Test extends ModuleTestBase
13+
public class ObjectArrayNullWrite10Test extends ModuleTestBase
1514
{
16-
private final CsvMapper MAPPER = mapperForCsv();
17-
1815
// for [dataformats-text#10]
19-
@JacksonTestFailureExpected
2016
@Test
2117
public void testNullsOnObjectArrayWrites2Col() throws Exception
2218
{
@@ -25,13 +21,14 @@ public void testNullsOnObjectArrayWrites2Col() throws Exception
2521
.addColumn("b", CsvSchema.ColumnType.NUMBER)
2622
.setUseHeader(true)
2723
.build();
28-
ObjectWriter writer = MAPPER.writer(schema);
24+
ObjectWriter writer = mapperForCsv().writer(schema);
2925
StringWriter out = new StringWriter();
30-
SequenceWriter sequence = writer.writeValues(out);
3126

32-
sequence.write(new Object[]{ null, 2 });
33-
sequence.write(new Object[]{ null, null });
34-
sequence.write(new Object[]{ 1, null });
27+
try (SequenceWriter sequence = writer.writeValues(out)) {
28+
sequence.write(new Object[]{ null, 2 });
29+
sequence.write(new Object[]{ null, null });
30+
sequence.write(new Object[]{ 1, null });
31+
}
3532

3633
final String csv = out.toString().trim();
3734

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.dataformat.csv.tofix;
1+
package com.fasterxml.jackson.dataformat.csv.ser;
22

33
import java.io.StringWriter;
44

@@ -7,41 +7,34 @@
77
import com.fasterxml.jackson.databind.ObjectWriter;
88
import com.fasterxml.jackson.databind.SequenceWriter;
99
import com.fasterxml.jackson.dataformat.csv.*;
10-
import com.fasterxml.jackson.dataformat.csv.testutil.failure.JacksonTestFailureExpected;
1110

1211
import static org.junit.jupiter.api.Assertions.assertEquals;
1312

14-
public class NullWriting116Test extends ModuleTestBase
13+
public class ObjectArrayNullWrite116Test extends ModuleTestBase
1514
{
16-
private final CsvMapper csv = mapperForCsv();
17-
18-
// [dataformat#116]
19-
@JacksonTestFailureExpected
15+
// for [dataformats-text#116]
2016
@Test
21-
public void testWithObjectArray() throws Exception
17+
public void testWithObjectArray() throws Exception
2218
{
2319
CsvSchema schema = CsvSchema.builder()
2420
.addColumn("a", CsvSchema.ColumnType.NUMBER)
2521
.addColumn("b", CsvSchema.ColumnType.NUMBER)
2622
.setUseHeader(true)
2723
.build();
28-
ObjectWriter writer = csv.writer(schema);
24+
ObjectWriter writer = mapperForCsv().writer(schema);
2925
StringWriter out = new StringWriter();
30-
SequenceWriter sequence = writer.writeValues(out);
31-
32-
sequence.write(new Object[]{ 1, 2 });
33-
// sequence.write(new Object[]{ null, 2 });
34-
sequence.write(new Object[]{ null, null });
35-
sequence.write(new Object[]{ 1, null });
3626

37-
sequence.close();
27+
try (SequenceWriter sequence = writer.writeValues(out)) {
28+
sequence.write(new Object[]{ 1, 2 });
29+
sequence.write(new Object[]{ null, 2 });
30+
sequence.write(new Object[]{ null, null });
31+
sequence.write(new Object[]{ 1, null });
32+
}
3833

39-
//System.err.println("CSV:\n"+out);
4034
assertEquals("a,b\n" +
4135
"1,2\n" +
42-
// ",2\n" +
36+
",2\n" +
4337
",\n" +
4438
"1,\n", out.toString());
4539
}
46-
4740
}

release-notes/VERSION-2.x

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ Active Maintainers:
1616

1717
2.21.0 (not yet released)
1818

19-
No changes since 2.20
19+
#10: Missing `null` writes when writing `Object[]` values
20+
(fix by @cowtowncoder, w/ Claude code)
2021

2122
2.20.1 (30-Oct-2025)
2223

0 commit comments

Comments
 (0)