From a6ee67ecf0d4b9208ffc640f433d8b40c258e1f3 Mon Sep 17 00:00:00 2001 From: OldTruckDriver Date: Fri, 19 Jun 2026 01:32:16 +1000 Subject: [PATCH] [CSV-328] Fix quoted null string after disabling quote setNullString(String) rebuilt quotedNullString by concatenating the nullable quoteCharacter field directly, so calling setQuote(null) before setNullString(...) produced a literal "nullNULLnull". Extract a shared setQuotedNullString() helper that applies the default-quote fallback, so both builder orders produce the same state. Reviewed-by: OpenAI Codex Reviewed-by: Anthropic Claude Code --- src/changes/changes.xml | 1 + src/main/java/org/apache/commons/csv/CSVFormat.java | 7 +++++-- src/test/java/org/apache/commons/csv/CSVFormatTest.java | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 66073c9dd..64f936554 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -53,6 +53,7 @@ CSVParser applies characterOffset to bytePosition (#604). CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. + CSVFormat.Builder.setNullString(String) can build an invalid quoted null string after setQuote(null). Escape Reader values with quote and escape (#606). Clear escape delimiter buffer before peek in Lexer.isEscapeDelimiter() (#608, #611). Escape quote char in printWithEscapes when QuoteMode is NONE (#609). diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 4f60eff93..9c403d9e1 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -780,8 +780,7 @@ public Builder setMaxRows(final long maxRows) { */ public Builder setNullString(final String nullString) { this.nullString = nullString; - this.quotedNullString = quoteCharacter + nullString + quoteCharacter; - return this; + return setQuotedNullString(); } /** @@ -806,6 +805,10 @@ public Builder setQuote(final Character quoteCharacter) { throw new IllegalArgumentException("The quoteCharacter cannot be a line break"); } this.quoteCharacter = quoteCharacter; + return setQuotedNullString(); + } + + private Builder setQuotedNullString() { final Character quote = quoteCharacter != null ? quoteCharacter : Constants.DOUBLE_QUOTE_CHAR; this.quotedNullString = quote + nullString + quote; return this; diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index c3fdeeb77..ed20898de 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -1040,6 +1040,11 @@ void testQuotedNullStringTracksQuoteCharacter() throws IOException { builder.setQuote((Character) null); builder.get().print(null, out, true); assertEquals("\"NULL\"", out.toString()); + // reset, reverse setter order + out.setLength(0); + builder.setNullString(null).setQuote((Character) null).setNullString("NULL"); + builder.get().print(null, out, true); + assertEquals("\"NULL\"", out.toString()); } @Test