diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index a6b8c2dd..1c77fc0f 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -63,6 +63,7 @@ public class CSVFormat implements Serializable { private boolean ignoreSurroundingSpaces; // Should leading/trailing spaces be ignored around values? private boolean ignoreEmptyLines; private String recordSeparator; // for outputs + private String nullToString; // for outputs private String[] header; /** @@ -74,7 +75,7 @@ public class CSVFormat implements Serializable { */ // package protected to give access without needing a synthetic accessor CSVFormatBuilder(final char delimiter){ - this(delimiter, null, null, null, null, false, false, null, null); + this(delimiter, null, null, null, null, false, false, null, Constants.EMPTY, null); } /** @@ -94,10 +95,11 @@ public class CSVFormat implements Serializable { * true when whitespaces enclosing values should be ignored * @param ignoreEmptyLines * true when the parser should skip empty lines - * @param recordSeparator - * the line separator to use for output + * @param nullToString TODO * @param header * the header + * @param recordSeparator + * the line separator to use for output * @throws IllegalArgumentException if the delimiter is a line break character */ // package protected for use by test code @@ -105,7 +107,7 @@ public class CSVFormat implements Serializable { final Quote quotePolicy, final Character commentStart, final Character escape, final boolean ignoreSurroundingSpaces, final boolean ignoreEmptyLines, final String lineSeparator, - final String[] header) { + String nullToString, final String[] header) { if (isLineBreak(delimiter)) { throw new IllegalArgumentException("The delimiter cannot be a line break"); } @@ -117,6 +119,7 @@ public class CSVFormat implements Serializable { this.ignoreSurroundingSpaces = ignoreSurroundingSpaces; this.ignoreEmptyLines = ignoreEmptyLines; this.recordSeparator = lineSeparator; + this.nullToString = nullToString; this.header = header; } @@ -132,7 +135,7 @@ public class CSVFormat implements Serializable { this(format.delimiter, format.quoteChar, format.quotePolicy, format.commentStart, format.escape, format.ignoreSurroundingSpaces, format.ignoreEmptyLines, - format.recordSeparator, format.header); + format.recordSeparator, format.nullToString, format.header); } /** @@ -143,7 +146,7 @@ public class CSVFormat implements Serializable { public CSVFormat build() { validate(); return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); + ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullToString, header); } /** @@ -327,6 +330,19 @@ public class CSVFormat implements Serializable { return this; } + /** + * Sets the String to use for null values for output. + * + * @param nullToString + * the String to use for null values for output. + * + * @return This builder with the the specified output record separator + */ + public CSVFormatBuilder withNullToString(final String nullToString) { + this.nullToString = nullToString; + return this; + } + /** * Sets the quoteChar of the format to the specified character. * @@ -423,7 +439,8 @@ public class CSVFormat implements Serializable { * @return a standard comma separated format builder, as for {@link #RFC4180} but allowing empty lines. */ public static CSVFormatBuilder newBuilder() { - return new CSVFormatBuilder(COMMA, DOUBLE_QUOTE_CHAR, null, null, null, false, true, CRLF, null); + return new CSVFormatBuilder(COMMA, DOUBLE_QUOTE_CHAR, null, null, null, false, true, CRLF, Constants.EMPTY, + null); } private final char delimiter; private final Character quoteChar; @@ -431,11 +448,12 @@ public class CSVFormat implements Serializable { private final Character commentStart; private final Character escape; private final boolean ignoreSurroundingSpaces; // Should leading/trailing spaces be ignored around values? - private final boolean ignoreEmptyLines; private final String recordSeparator; // for outputs + private final String nullToString; // for outputs + private final String[] header; /** @@ -570,6 +588,7 @@ public class CSVFormat implements Serializable { * true when the parser should skip empty lines * @param recordSeparator * the line separator to use for output + * @param nullToString TODO * @param header * the header * @throws IllegalArgumentException if the delimiter is a line break character @@ -579,7 +598,7 @@ public class CSVFormat implements Serializable { final Quote quotePolicy, final Character commentStart, final Character escape, final boolean ignoreSurroundingSpaces, final boolean ignoreEmptyLines, final String recordSeparator, - final String[] header) { + String nullToString, final String[] header) { if (isLineBreak(delimiter)) { throw new IllegalArgumentException("The delimiter cannot be a line break"); } @@ -591,6 +610,7 @@ public class CSVFormat implements Serializable { this.ignoreSurroundingSpaces = ignoreSurroundingSpaces; this.ignoreEmptyLines = ignoreEmptyLines; this.recordSeparator = recordSeparator; + this.nullToString = nullToString; this.header = header == null ? null : header.clone(); } @@ -722,6 +742,15 @@ public class CSVFormat implements Serializable { return ignoreSurroundingSpaces; } + /** + * Returns the value to use for writing null values. + * + * @return the value to use for writing null values. + */ + public String getNullToString() { + return nullToString; + } + /** * Returns the character used to encapsulate values containing special characters. * diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 52e74c4d..30ec9d46 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -36,7 +36,7 @@ import java.util.NoSuchElementException; * specification of a {@link CSVFormat}. * *

- * To parse a CSV input with tabs as separators, '"' (double-quote) as an optional value encapsulator, + * To parse a CSV input with tabs as separators, '"' (double-quote) as an optional value encapsulator, * and comments starting with '#', you write: *

* diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java index 5e980516..c9c5523f 100644 --- a/src/main/java/org/apache/commons/csv/CSVPrinter.java +++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java @@ -67,7 +67,7 @@ public class CSVPrinter implements Flushable, Closeable { /** * Outputs the line separator. - * + * * @throws IOException * If an I/O error occurs */ @@ -344,7 +344,7 @@ public class CSVPrinter implements Flushable, Closeable { */ public void print(final Object value) throws IOException { // null values are considered empty - final String strValue = value == null ? EMPTY : value.toString(); + final String strValue = value == null ? format.getNullToString() : value.toString(); print(value, strValue, 0, strValue.length()); } diff --git a/src/test/java/org/apache/commons/csv/CSVFormatBuilderTest.java b/src/test/java/org/apache/commons/csv/CSVFormatBuilderTest.java index 59c6b724..4eac0001 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatBuilderTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatBuilderTest.java @@ -41,7 +41,7 @@ public class CSVFormatBuilderTest { @Before public void setUp() throws Exception { - builder = new CSVFormatBuilder('+', Character.valueOf('!'), null, Character.valueOf('#'), Character.valueOf('!'), true, true, CRLF, null); + builder = new CSVFormatBuilder('+', Character.valueOf('!'), null, Character.valueOf('#'), Character.valueOf('!'), true, true, CRLF, Constants.EMPTY, null); } @Test diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index 6e224636..c5ac1cd2 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -307,6 +307,15 @@ public class CSVPrinterTest { printer.close(); } + @Test + public void testPrintCustomNullValues() throws IOException { + final StringWriter sw = new StringWriter(); + final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.toBuilder().withNullToString("NULL").build()); + printer.printRecord("a", null, "b"); + assertEquals("a,NULL,b" + recordSeparator, sw.toString()); + printer.close(); + } + @Test public void testQuoteAll() throws IOException { final StringWriter sw = new StringWriter();