From 98d2f1a07cf5da9d84f097b68856b3b6235243cd Mon Sep 17 00:00:00 2001 From: Cowtowncoder Date: Mon, 29 Jun 2015 15:29:51 -0700 Subject: [PATCH] One more fix wrt #72, to distinguish between empty String, null, when reading; only former will cause coercion of empty String into `null`, at streaming parser level (higher-level mapping may still occur). --- .../jackson/dataformat/csv/CsvGenerator.java | 2 +- .../jackson/dataformat/csv/CsvSchema.java | 42 +++++++++++++++---- .../dataformat/csv/impl/CsvEncoder.java | 4 +- .../dataformat/csv/deser/NullReadTest.java | 10 ++--- 4 files changed, 41 insertions(+), 17 deletions(-) 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 3ed1077..e692f51 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java @@ -643,7 +643,7 @@ public void writeNull() throws IOException _verifyValueWrite("write null value"); if (!_skipValue) { if (_arraySeparator >= 0) { - _addToArray(_schema.getNullValue()); + _addToArray(_schema.getNullValueOrEmpty()); } else if (_writeContext.inRoot()) { // as per [#69] // or, to write 'empty Object' (for common case), would // write single null, then finish row, like so: diff --git a/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvSchema.java b/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvSchema.java index 8649347..a13f48f 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvSchema.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvSchema.java @@ -84,6 +84,8 @@ public class CsvSchema protected final static int DEFAULT_ENCODING_FEATURES = 0; + protected final static char[] NO_CHARS = new char[0]; + /* /********************************************************************** /* Constants, default settings @@ -100,13 +102,20 @@ public class CsvSchema * semicolon. */ public final static char DEFAULT_ARRAY_ELEMENT_SEPARATOR = ';'; - + public final static char DEFAULT_QUOTE_CHAR = '"'; /** - * By default, nulls are written as empty Strings ("") + * By default, nulls are written as empty Strings (""); and no coercion + * is performed from any String (higher level databind may, however, + * coerce Strings into Java nulls). + * To use automatic coercion on reading, null value must be set explicitly + * to empty String (""). + *

+ * NOTE: before 2.6, this value default to empty char[]; changed + * to Java null in 2.6. */ - public final static char[] DEFAULT_NULL_VALUE = new char[0]; + public final static char[] DEFAULT_NULL_VALUE = null; /** * By default, no escape character is used -- this is denoted by @@ -549,7 +558,7 @@ public Builder setNullValue(String nvl) { } public Builder setNullValue(char[] nvl) { - _nullValue = (nvl == null) ? DEFAULT_NULL_VALUE : nvl; + _nullValue = nvl; return this; } @@ -888,7 +897,7 @@ public CsvSchema withNullValue(String nvl) { return new CsvSchema(_columns, _features, _columnSeparator, _quoteChar, _escapeChar, _lineSeparator, _arrayElementSeparator, - (nvl == null) ? DEFAULT_NULL_VALUE : nvl.toCharArray(), + (nvl == null) ? null : nvl.toCharArray(), _columnsByName); } @@ -982,21 +991,36 @@ public String getSchemaType() { public char[] getLineSeparator() { return _lineSeparator; } /** + * @return Null value defined, as char array, if one is defined to be recognized; Java null + * if not. + * * @since 2.5 */ public char[] getNullValue() { return _nullValue; } + /** + * Same as {@link #getNullValue()} except that undefined null value (one that remains as null, + * or explicitly set as such) will be returned as empty char[] + * + * @since 2.6 + */ + public char[] getNullValueOrEmpty() { + if (_nullValue == null) { + return NO_CHARS; + } + return _nullValue; + } + /** * @since 2.6 */ public String getNullValueString() { String str = _nullValueAsString; if (str == null) { - if (_nullValue == null || _nullValue.length == 0) { - str = ""; - } else { - str = new String(_nullValue); + if (_nullValue == null) { + return null; } + str = (_nullValue.length == 0) ? "" : new String(_nullValue); _nullValueAsString = str; } return str; 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 50e07b4..285ad55 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 @@ -171,7 +171,7 @@ public CsvEncoder(IOContext ctxt, int csvFeatures, Writer out, CsvSchema schema) _cfgQuoteCharacter = schema.getQuoteChar(); _cfgLineSeparator = schema.getLineSeparator(); _cfgLineSeparatorLength = (_cfgLineSeparator == null) ? 0 : _cfgLineSeparator.length; - _cfgNullValue = schema.getNullValue(); + _cfgNullValue = schema.getNullValueOrEmpty(); _columnCount = schema.size(); @@ -198,7 +198,7 @@ public CsvEncoder(CsvEncoder base, CsvSchema newSchema) _cfgQuoteCharacter = newSchema.getQuoteChar(); _cfgLineSeparator = newSchema.getLineSeparator(); _cfgLineSeparatorLength = _cfgLineSeparator.length; - _cfgNullValue = newSchema.getNullValue(); + _cfgNullValue = newSchema.getNullValueOrEmpty(); _cfgMinSafeChar = _calcSafeChar(); _columnCount = newSchema.size(); } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/NullReadTest.java b/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/NullReadTest.java index de3e6cd..407aa43 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/NullReadTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/NullReadTest.java @@ -50,19 +50,19 @@ public void testReadNullValueFromEmptyString() throws Exception String csv = MAPPER.writer(schemaWithDefault).writeValueAsString(new IdDesc("id", null)); assertEquals("id,\n", csv); - // but read back + // but read back. Note: no null coercion unless explicitly defined ObjectReader r = MAPPER.readerFor(IdDesc.class).with(schemaWithDefault); IdDesc result = r.readValue(csv); assertNotNull(result); assertEquals("id", result.id); - assertNull(result.desc); + assertEquals("", result.desc); // also try the other combination result = r.readValue(",Whatevs\n"); assertNotNull(result); - assertNull(result.id); + assertEquals("", result.id); assertEquals("Whatevs", result.desc); // And then with explicit Empty String @@ -80,7 +80,7 @@ public void testReadNullValueFromEmptyString() throws Exception assertEquals("id", result.id); assertNull(result.desc); - // and finally with explicit `null` + // and finally with explicit `null`, which once again disables coercion CsvSchema schemaWithExplicitNull = CsvSchema.builder() .setNullValue((String) null) .addColumn("id") @@ -93,6 +93,6 @@ public void testReadNullValueFromEmptyString() throws Exception result = r.readValue(csv); assertNotNull(result); assertEquals("id", result.id); - assertNull(result.desc); + assertEquals("", result.desc); } }