From 36ace7c427e995032ee1cf0e4db3d30eb63e56ca Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 30 Sep 2020 13:44:47 -0700 Subject: [PATCH] More refactoring for better XML/CSV support for timestamp-based values --- .../jsr310/deser/DurationDeserializer.java | 36 ++++++++++++------- .../jsr310/deser/JSR310DeserializerBase.java | 9 +++++ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/DurationDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/DurationDeserializer.java index cdc98a4e..ec1627d6 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/DurationDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/DurationDeserializer.java @@ -20,6 +20,8 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.core.StreamReadCapability; +import com.fasterxml.jackson.core.io.NumberInput; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -89,12 +91,8 @@ public Duration deserialize(JsonParser parser, DeserializationContext context) t case JsonTokenId.ID_NUMBER_FLOAT: BigDecimal value = parser.getDecimalValue(); return DecimalUtils.extractSecondsAndNanos(value, Duration::ofSeconds); - case JsonTokenId.ID_NUMBER_INT: - if(context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) { - return Duration.ofSeconds(parser.getLongValue()); - } - return Duration.ofMillis(parser.getLongValue()); + return _fromTimestamp(context, parser.getLongValue()); case JsonTokenId.ID_STRING: return _fromString(parser, context, parser.getText()); // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) @@ -113,20 +111,34 @@ public Duration deserialize(JsonParser parser, DeserializationContext context) t JsonToken.VALUE_NUMBER_INT, JsonToken.VALUE_NUMBER_FLOAT); } - protected Duration _fromString(JsonParser parser, DeserializationContext context, - String string) throws IOException + protected Duration _fromString(JsonParser parser, DeserializationContext ctxt, + String value) throws IOException { - string = string.trim(); - if (string.length() == 0) { + value = value.trim(); + if (value.length() == 0) { if (!isLenient()) { - return _failForNotLenient(parser, context, JsonToken.VALUE_STRING); + return _failForNotLenient(parser, ctxt, JsonToken.VALUE_STRING); } return null; } + // 30-Sep-2020: Should allow use of "Timestamp as String" for + // some textual formats + if (ctxt.isEnabled(StreamReadCapability.UNTYPED_SCALARS) + && _isValidTimestampString(value)) { + return _fromTimestamp(ctxt, NumberInput.parseLong(value)); + } + try { - return Duration.parse(string); + return Duration.parse(value); } catch (DateTimeException e) { - return _handleDateTimeException(context, e, string); + return _handleDateTimeException(ctxt, e, value); + } + } + + protected Duration _fromTimestamp(DeserializationContext ctxt, long ts) { + if (ctxt.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) { + return Duration.ofSeconds(ts); } + return Duration.ofMillis(ts); } } diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DeserializerBase.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DeserializerBase.java index 35616cf1..ec208b72 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DeserializerBase.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DeserializerBase.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.io.NumberInput; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonMappingException; @@ -108,6 +109,14 @@ public Object deserializeWithType(JsonParser parser, DeserializationContext cont return typeDeserializer.deserializeTypedFromAny(parser, context); } + // @since 2.12 + protected boolean _isValidTimestampString(String str) { + // 30-Sep-2020, tatu: Need to support "numbers as Strings" for data formats + // that only have String values for scalars (CSV, Properties, XML) + // NOTE: we do allow negative values, but has to fit in 64-bits: + return _isIntNumber(str) && NumberInput.inLongRange(str, (str.charAt(0) == '-')); + } + protected BOGUS _reportWrongToken(DeserializationContext context, JsonToken exp, String unit) throws IOException {