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 baaea74..2c454d8 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java @@ -64,6 +64,12 @@ public enum Feature * @since 2.5 */ ALWAYS_QUOTE_STRINGS(false), + + /** + * Feature that determines whether values written as empty Strings (from java.lang.String + * valued POJO properties) should be forced to be quoted. + */ + ALWAYS_QUOTE_EMPTY_STRINGS(false), ; protected final boolean _defaultState; diff --git a/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/CsvEncoder.java b/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/CsvEncoder.java index 695bd67..e8119ab 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/CsvEncoder.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/CsvEncoder.java @@ -88,6 +88,8 @@ public class CsvEncoder * @since 2.5 */ protected boolean _cfgAlwaysQuoteStrings; + + protected boolean _cfgAlwaysQuoteEmptyStrings; /* /********************************************************** @@ -166,6 +168,7 @@ public CsvEncoder(IOContext ctxt, int csvFeatures, Writer out, CsvSchema schema) _cfgOptimalQuoting = CsvGenerator.Feature.STRICT_CHECK_FOR_QUOTING.enabledIn(csvFeatures); _cfgIncludeMissingTail = !CsvGenerator.Feature.OMIT_MISSING_TAIL_COLUMNS.enabledIn(_csvFeatures); _cfgAlwaysQuoteStrings = CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS.enabledIn(csvFeatures); + _cfgAlwaysQuoteEmptyStrings = CsvGenerator.Feature.ALWAYS_QUOTE_EMPTY_STRINGS.enabledIn(csvFeatures); _outputBuffer = ctxt.allocConcatBuffer(); _bufferRecyclable = true; @@ -193,7 +196,8 @@ public CsvEncoder(CsvEncoder base, CsvSchema newSchema) _cfgOptimalQuoting = base._cfgOptimalQuoting; _cfgIncludeMissingTail = base._cfgIncludeMissingTail; _cfgAlwaysQuoteStrings = base._cfgAlwaysQuoteStrings; - + _cfgAlwaysQuoteEmptyStrings = base._cfgAlwaysQuoteEmptyStrings; + _outputBuffer = base._outputBuffer; _bufferRecyclable = base._bufferRecyclable; _outputEnd = base._outputEnd; @@ -232,6 +236,7 @@ public CsvEncoder overrideFormatFeatures(int feat) { _cfgOptimalQuoting = CsvGenerator.Feature.STRICT_CHECK_FOR_QUOTING.enabledIn(feat); _cfgIncludeMissingTail = !CsvGenerator.Feature.OMIT_MISSING_TAIL_COLUMNS.enabledIn(feat); _cfgAlwaysQuoteStrings = CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS.enabledIn(feat); + _cfgAlwaysQuoteEmptyStrings = CsvGenerator.Feature.ALWAYS_QUOTE_EMPTY_STRINGS.enabledIn(feat); } return this; } @@ -870,6 +875,9 @@ protected boolean _mayNeedQuotes(String value, int length) if (_cfgEscapeCharacter > 0) { return _needsQuotingLoose(value, _cfgEscapeCharacter); } + if (_cfgAlwaysQuoteEmptyStrings && length == 0) { + return true; + } return _needsQuotingLoose(value); } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/TestGenerator.java b/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/TestGenerator.java index db78eca..39e3c3a 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/TestGenerator.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/TestGenerator.java @@ -190,20 +190,39 @@ public void testForcedQuoting60() throws Exception CsvMapper mapper = mapperForCsv(); mapper.enable(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS); CsvSchema schema = CsvSchema.builder() - .addColumn("id") - .addColumn("amount") - .build(); + .addColumn("id") + .addColumn("amount") + .build(); String result = mapper.writer(schema) - .writeValueAsString(new Entry("abc", 1.25)); + .writeValueAsString(new Entry("abc", 1.25)); assertEquals("\"abc\",1.25\n", result); // Also, as per [dataformat-csv#81], should be possible to change dynamically result = mapper.writer(schema) - .without(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS) - .writeValueAsString(new Entry("xyz", 2.5)); + .without(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS) + .writeValueAsString(new Entry("xyz", 2.5)); assertEquals("xyz,2.5\n", result); } + public void testForcedQuotingEmptyStrings() throws Exception + { + CsvMapper mapper = mapperForCsv(); + mapper.enable(CsvGenerator.Feature.ALWAYS_QUOTE_EMPTY_STRINGS); + CsvSchema schema = CsvSchema.builder() + .addColumn("id") + .addColumn("amount") + .build(); + String result = mapper.writer(schema) + .writeValueAsString(new Entry("", 1.25)); + assertEquals("\"\",1.25\n", result); + + // Also, as per [dataformat-csv#81], should be possible to change dynamically + result = mapper.writer(schema) + .without(CsvGenerator.Feature.ALWAYS_QUOTE_EMPTY_STRINGS) + .writeValueAsString(new Entry("", 2.5)); + assertEquals(",2.5\n", result); + } + // Must comment '#', at least if it starts the line public void testQuotingOfCommentChar() throws Exception {