diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b91a71a9..557e4df6 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -38,7 +38,8 @@ Release Notes - + + withNullString value is printed without quotes when QuoteMode.ALL is specified; add QuoteMode.ALL_NON_NULL. PR #17. Fix outdated comments about FileReader in CSVParser #13 Fix incorrect method name 'withFirstRowAsHeader' in user guide. Negative numeric values in the first column are always quoted in minimal mode. diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 79a0713e..3d3e7e5e 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -946,7 +946,16 @@ public final class CSVFormat implements Serializable { // Only call CharSequence.toString() if you have to, helps GC-free use cases. CharSequence charSequence; if (value == null) { - charSequence = nullString == null ? Constants.EMPTY : nullString; + // https://issues.apache.org/jira/browse/CSV-203 + if (null == nullString) { + charSequence = Constants.EMPTY; + } else { + if (QuoteMode.ALL == quoteMode) { + charSequence = quoteCharacter + nullString + quoteCharacter; + } else { + charSequence = nullString; + } + } } else { charSequence = value instanceof CharSequence ? (CharSequence) value : value.toString(); } @@ -1031,6 +1040,7 @@ public final class CSVFormat implements Serializable { } switch (quoteModePolicy) { case ALL: + case ALL_NON_NULL: quote = true; break; case NON_NUMERIC: diff --git a/src/main/java/org/apache/commons/csv/QuoteMode.java b/src/main/java/org/apache/commons/csv/QuoteMode.java index b0a31c23..08a9b725 100644 --- a/src/main/java/org/apache/commons/csv/QuoteMode.java +++ b/src/main/java/org/apache/commons/csv/QuoteMode.java @@ -28,6 +28,11 @@ public enum QuoteMode { */ ALL, + /** + * Quotes all non-null fields. + */ + ALL_NON_NULL, + /** * Quotes fields which contain special characters such as a delimiter, quotes character or any of the characters in * line separator. diff --git a/src/test/java/org/apache/commons/csv/issues/JiraCsv203Test.java b/src/test/java/org/apache/commons/csv/issues/JiraCsv203Test.java new file mode 100644 index 00000000..1e3c067f --- /dev/null +++ b/src/test/java/org/apache/commons/csv/issues/JiraCsv203Test.java @@ -0,0 +1,111 @@ +package org.apache.commons.csv.issues; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.QuoteMode; +import org.junit.Assert; +import org.junit.Test; + +/** + * JIRA: withNullString value is printed without quotes when QuoteMode.ALL is specified + */ +public class JiraCsv203Test { + + @Test + public void testQuoteModeAll() throws Exception { + CSVFormat format = CSVFormat.EXCEL + .withNullString("N/A") + .withIgnoreSurroundingSpaces(true) + .withQuoteMode(QuoteMode.ALL); + + StringBuffer buffer = new StringBuffer(); + CSVPrinter printer = new CSVPrinter(buffer, format); + printer.printRecord(new Object[] { null, "Hello", null, "World" }); + + Assert.assertEquals("\"N/A\",\"Hello\",\"N/A\",\"World\"\r\n", buffer.toString()); + } + + @Test + public void testQuoteModeAllNonNull() throws Exception { + CSVFormat format = CSVFormat.EXCEL + .withNullString("N/A") + .withIgnoreSurroundingSpaces(true) + .withQuoteMode(QuoteMode.ALL_NON_NULL); + + StringBuffer buffer = new StringBuffer(); + CSVPrinter printer = new CSVPrinter(buffer, format); + printer.printRecord(new Object[] { null, "Hello", null, "World" }); + + Assert.assertEquals("N/A,\"Hello\",N/A,\"World\"\r\n", buffer.toString()); + } + + @Test + public void testWithoutQuoteMode() throws Exception { + CSVFormat format = CSVFormat.EXCEL + .withNullString("N/A") + .withIgnoreSurroundingSpaces(true); + + StringBuffer buffer = new StringBuffer(); + CSVPrinter printer = new CSVPrinter(buffer, format); + printer.printRecord(new Object[] { null, "Hello", null, "World" }); + + Assert.assertEquals("N/A,Hello,N/A,World\r\n", buffer.toString()); + } + + @Test + public void testQuoteModeMinimal() throws Exception { + CSVFormat format = CSVFormat.EXCEL + .withNullString("N/A") + .withIgnoreSurroundingSpaces(true) + .withQuoteMode(QuoteMode.MINIMAL); + + StringBuffer buffer = new StringBuffer(); + CSVPrinter printer = new CSVPrinter(buffer, format); + printer.printRecord(new Object[] { null, "Hello", null, "World" }); + + Assert.assertEquals("N/A,Hello,N/A,World\r\n", buffer.toString()); + } + + @Test + public void testQuoteModeNonNumeric() throws Exception { + CSVFormat format = CSVFormat.EXCEL + .withNullString("N/A") + .withIgnoreSurroundingSpaces(true) + .withQuoteMode(QuoteMode.NON_NUMERIC); + + StringBuffer buffer = new StringBuffer(); + CSVPrinter printer = new CSVPrinter(buffer, format); + printer.printRecord(new Object[] { null, "Hello", null, "World" }); + + Assert.assertEquals("N/A,\"Hello\",N/A,\"World\"\r\n", buffer.toString()); + } + + @Test + public void testWithoutNullString() throws Exception { + CSVFormat format = CSVFormat.EXCEL + //.withNullString("N/A") + .withIgnoreSurroundingSpaces(true) + .withQuoteMode(QuoteMode.ALL); + + StringBuffer buffer = new StringBuffer(); + CSVPrinter printer = new CSVPrinter(buffer, format); + printer.printRecord(new Object[] { null, "Hello", null, "World" }); + + Assert.assertEquals(",\"Hello\",,\"World\"\r\n", buffer.toString()); + } + + @Test + public void testWithEmptyValues() throws Exception { + CSVFormat format = CSVFormat.EXCEL + .withNullString("N/A") + .withIgnoreSurroundingSpaces(true) + .withQuoteMode(QuoteMode.ALL); + + StringBuffer buffer = new StringBuffer(); + CSVPrinter printer = new CSVPrinter(buffer, format); + printer.printRecord(new Object[] { "", "Hello", "", "World" }); + //printer.printRecord(new Object[] { null, "Hello", null, "World" }); + + Assert.assertEquals("\"\",\"Hello\",\"\",\"World\"\r\n", buffer.toString()); + } +} \ No newline at end of file