Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gracefully handle Deserialization of Joda values from JSON Objects #115

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;

Expand Down Expand Up @@ -72,6 +73,14 @@ public DateMidnight deserialize(JsonParser p, DeserializationContext ctxt)
return null;
}
return local.toDateMidnight();
case JsonTokenId.ID_START_OBJECT:
JsonNode treeNode = p.readValueAsTree();
long millis = treeNode.path("millis").asLong(Long.MIN_VALUE);
String id = treeNode.path("zone").path("ID").asText();
if (millis >= 0) {
DateTimeZone tz = DateTimeZone.forID(id);
return new DateMidnight(millis, tz);
}
default:
}
return (DateMidnight) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;

Expand Down Expand Up @@ -93,6 +94,14 @@ public ReadableInstant deserialize(JsonParser p, DeserializationContext ctxt)
// Not sure if it should use timezone or not...
// 15-Sep-2015, tatu: impl of 'createParser()' SHOULD handle all timezone/locale setup
return _format.createParser(ctxt).parseDateTime(str);
case JsonTokenId.ID_START_OBJECT:
JsonNode treeNode = p.readValueAsTree();
long millis = treeNode.path("millis").asLong(Long.MIN_VALUE);
String id = treeNode.path("zone").path("ID").asText();
if (millis >= 0) {
tz = DateTimeZone.forID(id);
return new DateTime(millis, tz);
}
}
return _handleNotNumberOrString(p, ctxt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;

/**
* Deserializer for Joda {@link DateTimeZone}.
Expand All @@ -23,9 +24,14 @@ public DateTimeZone deserialize(JsonParser p, DeserializationContext ctxt)
if (t == JsonToken.VALUE_NUMBER_INT) {
// for fun let's allow use of offsets...
return DateTimeZone.forOffsetHours(p.getIntValue());
}
if (t == JsonToken.VALUE_STRING) {
} else if (t == JsonToken.VALUE_STRING) {
return DateTimeZone.forID(p.getText().trim());
} else if (t == JsonToken.START_OBJECT) {
JsonNode treeNode = p.readValueAsTree();
String id = treeNode.path("ID").asText();
if (id != null && !id.isEmpty()) {
return DateTimeZone.forID(id);
}
}
return _handleNotNumberOrString(p, ctxt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaPeriodFormat;

Expand Down Expand Up @@ -38,6 +39,12 @@ public Duration deserialize(JsonParser p, DeserializationContext ctxt) throws IO
return new Duration(p.getLongValue());
case JsonTokenId.ID_STRING:
return _format.parsePeriod(ctxt, p.getText().trim()).toStandardDuration();
case JsonTokenId.ID_START_OBJECT:
JsonNode treeNode = p.readValueAsTree();
long millis = treeNode.path("millis").asLong(Long.MIN_VALUE);
if (millis >= 0) {
return new Duration(millis);
}
default:
}
return _handleNotNumberOrString(p, ctxt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public Instant deserialize(JsonParser p, DeserializationContext ctxt) throws IOE
// 11-Sep-2018, tatu: `DateTimeDeserializer` allows timezone inclusion in brackets;
// should that be checked here too?
return Instant.parse(str, _format.createParser(ctxt));
case JsonTokenId.ID_START_OBJECT:
JsonNode treeNode = p.readValueAsTree();
long millis = treeNode.path("millis").asLong(Long.MIN_VALUE);
if (millis >= 0) {
return new Instant(millis);
}
default:
}
return _handleNotNumberOrString(p, ctxt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.fasterxml.jackson.core.JsonToken;

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;

Expand All @@ -33,6 +34,16 @@ public Interval deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
if (!p.hasToken(JsonToken.VALUE_STRING)) {
if (p.hasToken(JsonToken.START_OBJECT)) {
JsonNode treeNode = p.readValueAsTree();
long startMillis = treeNode.path("startMillis").asLong(Long.MIN_VALUE);
long endMillis = treeNode.path("endMillis").asLong(Long.MIN_VALUE);
String id = treeNode.path("chronology").path("zone").path("ID").asText();
if (startMillis >= 0 && endMillis >= 0 && startMillis <= endMillis) {
DateTimeZone tz = DateTimeZone.forID(id);
return new Interval(startMillis, endMillis, tz);
}
}
return (Interval) ctxt.handleUnexpectedToken(getValueType(ctxt),
p.currentToken(), p, "expected JSON String");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.fasterxml.jackson.core.JsonTokenId;

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;

Expand Down Expand Up @@ -60,6 +61,14 @@ public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws I
throw ctxt.wrongTokenException(p, handledType(), JsonToken.END_ARRAY, "after LocalDate ints");
}
return new LocalDate(year, month, day);
case JsonTokenId.ID_START_OBJECT:
JsonNode treeNode = p.readValueAsTree();
int localDateYear = treeNode.path("year").asInt(Integer.MIN_VALUE);
int localDateMonth = treeNode.path("monthOfYear").asInt(Integer.MIN_VALUE);
int localDateDay = treeNode.path("dayOfMonth").asInt(Integer.MIN_VALUE);
if (localDateYear != Integer.MIN_VALUE && localDateMonth >= 0 && localDateDay >= 0) {
return new LocalDate(localDateYear, localDateMonth, localDateDay);
}
}
return (LocalDate) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p,
"expected String, Number or JSON Array");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.fasterxml.jackson.core.*;

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;

Expand Down Expand Up @@ -78,6 +79,20 @@ public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
return dt;
}
throw ctxt.wrongTokenException(p, handledType(), JsonToken.END_ARRAY, "after LocalDateTime ints");
case JsonTokenId.ID_START_OBJECT:
JsonNode treeNode = p.readValueAsTree();
int year = treeNode.path("year").asInt(Integer.MIN_VALUE);
int month = treeNode.path("monthOfYear").asInt(Integer.MIN_VALUE);
int day = treeNode.path("dayOfMonth").asInt(Integer.MIN_VALUE);
int hourOfDay = treeNode.path("hourOfDay").asInt(Integer.MIN_VALUE);
int minuteOfHour = treeNode.path("minuteOfHour").asInt(Integer.MIN_VALUE);
int secondOfMinute = treeNode.path("secondOfMinute").asInt(Integer.MIN_VALUE);
int millisOfSecond = treeNode.path("millisOfSecond").asInt(0); // optional and defaults to zero
if (year != Integer.MIN_VALUE && month >= 0 && day >= 0
&& hourOfDay >= 0 && minuteOfHour >= 0 && secondOfMinute >= 0) {
return new LocalDateTime(year, month, day,
hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
}
default:
}
return (LocalDateTime) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.datatype.joda.cfg.FormatConfig;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;

Expand Down Expand Up @@ -38,6 +39,15 @@ public LocalTime deserialize(JsonParser p, DeserializationContext ctxt)
String str = p.getText().trim();
return (str.length() == 0) ? null
: _format.createParser(ctxt).parseLocalTime(str);
case JsonTokenId.ID_START_OBJECT:
JsonNode treeNode = p.readValueAsTree();
int hourOfDay = treeNode.path("hourOfDay").asInt(Integer.MIN_VALUE);
int minuteOfHour = treeNode.path("minuteOfHour").asInt(Integer.MIN_VALUE);
int secondOfMinute = treeNode.path("secondOfMinute").asInt(Integer.MIN_VALUE);
int millisOfSecond = treeNode.path("millisOfSecond").asInt(0); // optional and defaults to zero
if (hourOfDay >= 0 && minuteOfHour >= 0 && secondOfMinute >= 0) {
return new LocalTime(hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
}
default:
}
// [HH,MM,ss,ms?]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;

/**
* A Jackson deserializer for Joda MonthDay objects.
Expand Down Expand Up @@ -39,6 +40,13 @@ public MonthDay deserialize(final JsonParser p, final DeserializationContext ctx
return (MonthDay) getNullValue(ctxt);
}
return MonthDay.parse(str, _format.createParser(ctxt));
} else if (p.hasToken(JsonToken.START_OBJECT)) {
JsonNode treeNode = p.readValueAsTree();
int month = treeNode.path("monthOfYear").asInt(Integer.MIN_VALUE);
int day = treeNode.path("dayOfMonth").asInt(Integer.MIN_VALUE);
if (month >= 0 && day >= 0) {
return new MonthDay(month, day);
}
}
return (MonthDay) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p,
"expected JSON String");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ else if (periodName.equals( "Months" )) {
}
else if (periodName.equals( "Years" )) {
rp = Years.years( periodValue );
}
else if (periodName.equals( "Standard" )) {
int years = treeNode.path("years").asInt(0);
int months = treeNode.path("months").asInt(0);
int weeks = treeNode.path("weeks").asInt(0);
int days = treeNode.path("days").asInt(0);
int hours = treeNode.path("hours").asInt(0);
int minutes = treeNode.path("minutes").asInt(0);
int seconds = treeNode.path("seconds").asInt(0);
int millis = treeNode.path("millis").asInt(0);
rp = new Period(years, months, weeks, days, hours, minutes, seconds, millis, PeriodType.standard());
} else {
ctxt.reportInputMismatch(handledType(),
"Don't know how to deserialize %s using periodName '%s'",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;

/**
* A Jackson deserializer for Joda YearMonth objects.
Expand Down Expand Up @@ -40,6 +41,13 @@ public YearMonth deserialize(final JsonParser p, final DeserializationContext ct
return null;
}
return YearMonth.parse(str, _format.createParser(ctxt));
} else if (t == JsonToken.START_OBJECT) {
JsonNode treeNode = p.readValueAsTree();
int year = treeNode.path("year").asInt(Integer.MIN_VALUE);
int month = treeNode.path("monthOfYear").asInt(Integer.MIN_VALUE);
if (year != Integer.MIN_VALUE && month != Integer.MIN_VALUE) {
return new YearMonth(year, month);
}
}
return (YearMonth) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p,
"expected JSON String");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ protected static interface MixinForPolymorphism {
/**********************************************************************
*/

protected static MapperBuilder<?,?> mapperBuilder() {
return JsonMapper.builder();
}

protected static MapperBuilder<?,?> mapperWithModuleBuilder() {
return JsonMapper.builder()
return mapperBuilder()
.addModule(new JodaModule());
}

Expand All @@ -71,6 +75,10 @@ protected static MapperBuilder<?,?> jodaMapperBuilder(TimeZone tz) {
.defaultTimeZone(tz);
}

protected static ObjectMapper mapper() {
return mapperBuilder().build();
}

protected static ObjectMapper mapperWithModule() {
return mapperWithModuleBuilder().build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,11 @@ public void testWithTimeZoneOverride() throws Exception
// Is this stable enough for testing?
assertEquals("America/New_York", resultTz.getID());
}

public void testDeserDateMidnightWhichWasSerializedWithoutJodaModule() throws IOException {
DateMidnight expectedDateMidnight = new DateMidnight(2020, 1, 1, DateTimeZone.forID("America/New_York"));
String json = mapper().writeValueAsString(expectedDateMidnight);
DateMidnight dateMidnight = mapperWithModule().readValue(json, DateMidnight.class);
assertEquals(expectedDateMidnight, dateMidnight);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,11 @@ public void test_disable_ADJUST_DATES_TO_CONTEXT_TIME_ZONE() throws Exception
DateTimeZone expTZ = DateTimeZone.forID("Asia/Shanghai");
assertEquals(new DateTime(2017, 1, 1, 1, 1, 1, expTZ), result);
}

public void testDeserDateTimeWhichWasSerializedWithoutJodaModule() throws IOException {
DateTime expectedDateTime = new DateTime(2020, 1, 1, 3, 22, 51, 229, DateTimeZone.forID("Asia/Shanghai"));
String json = mapper().writeValueAsString(expectedDateTime);
DateTime dateTime = mapperWithModule().readValue(json, DateTime.class);
assertEquals(expectedDateTime, dateTime);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fasterxml.jackson.datatype.joda.deser;

import java.io.IOException;
import java.util.TimeZone;

import org.joda.time.DateTimeZone;
Expand Down Expand Up @@ -45,4 +46,12 @@ public void testSimpleDateTimeZone() throws Exception
assertNotNull(result2.tz);
assertEquals(input, result2.tz);
}

public void testDeserDateTimeZoneWhichWasSerializedWithoutJodaModule() throws IOException {
TimeZone timeZone = TimeZone.getTimeZone("GMT-7");
DateTimeZone expectedDateTimeZone = DateTimeZone.forTimeZone(timeZone);
String json = mapper().writeValueAsString(expectedDateTimeZone);
DateTimeZone dateTimeZone = mapperWithModule().readValue(json, DateTimeZone.class);
assertEquals(expectedDateTimeZone, dateTimeZone);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,12 @@ public void testDurationAltKeyDeserialize() throws IOException
assertNotNull(map);
assertTrue(map.containsKey(Duration.standardMinutes(4 * 60 + 30)));
}

public void testDeserDurationWhichWasSerializedWithoutJodaModule() throws IOException {
long millisInAnHour = 60 * 60 * 1000;
Duration expectedDuration = new Duration(millisInAnHour);
String json = mapper().writeValueAsString(expectedDuration);
Duration duration = mapperWithModule().readValue(json, Duration.class);
assertEquals(expectedDuration, duration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,15 @@ public void testDeserInstantCustomFormat() throws IOException
assertEquals(1972, date.getYear());
assertEquals(789, date.getMillisOfSecond());
}

public void testDeserInstantWhichWasSerializedWithoutJodaModule() throws IOException {
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.set(2020, Calendar.JUNE, 20, 0, 0, 0);
cal.set(Calendar.MILLISECOND, 0);
long timepoint = cal.getTime().getTime();
Instant expectedInstant = new Instant(timepoint);
String json = mapper().writeValueAsString(expectedInstant);
Instant instant = mapperWithModule().readValue(json, Instant.class);
assertEquals(expectedInstant, instant);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,13 @@ public void testIntervalDeserFromIso8601WithTimezoneWithDefaultTimeZone() throws

assertEquals(expectedInterval, actualInterval);
}

public void testDeserIntervalWhichWasSerializedWithoutJodaModule() throws IOException {
long start = 1396439982;
long end = 1396440001;
Interval expectedInterval = new Interval(start, end, DateTimeZone.forID("America/New_York"));
String json = mapper().writeValueAsString(expectedInterval);
Interval interval = mapperWithModule().readValue(json, Interval.class);
assertEquals(expectedInterval, interval);
}
}
Loading