Skip to content
This repository has been archived by the owner on Nov 7, 2019. It is now read-only.

PR to resolve #13 - ISO 8601 serialization of types ZonedDateTime and OffsetDateTime #15

Merged
merged 13 commits into from
Jun 1, 2015
Merged
Show file tree
Hide file tree
Changes from 7 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 @@ -23,6 +23,7 @@

import java.io.IOException;
import java.time.temporal.Temporal;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;

Expand All @@ -37,13 +38,23 @@ public class InstantSerializerBase<T extends Temporal> extends JSR310SerializerB

private final ToIntFunction<T> getNanoseconds;

private final Function<T, String> toIsoString;

protected InstantSerializerBase(Class<T> supportedType, ToLongFunction<T> getEpochMillis,
ToLongFunction<T> getEpochSeconds, ToIntFunction<T> getNanoseconds)
{
this(supportedType, getEpochMillis, getEpochSeconds, getNanoseconds, t -> t.toString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as a comment below: s/t -> t.toString()/t::toString/, compiler to confirm if syntax is correct.

}

protected InstantSerializerBase(Class<T> supportedType, ToLongFunction<T> getEpochMillis,
ToLongFunction<T> getEpochSeconds, ToIntFunction<T> getNanoseconds)
ToLongFunction<T> getEpochSeconds, ToIntFunction<T> getNanoseconds,
Function<T, String> toIsoString)
{
super(supportedType);
this.getEpochMillis = getEpochMillis;
this.getEpochSeconds = getEpochSeconds;
this.getNanoseconds = getNanoseconds;
this.toIsoString = toIsoString;
}

@Override
Expand All @@ -66,7 +77,7 @@ public void serialize(T instant, JsonGenerator generator, SerializerProvider pro
}
else
{
generator.writeString(instant.toString());
generator.writeString(this.toIsoString.apply(instant));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please disregard this. I misunderstood in which file this change was taking place.

I don't understand why this change is necessary. According to the API documentation, Instant#toString already outputs a strictly ISO-compliant string.

}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.fasterxml.jackson.datatype.jsr310.ser;

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class OffsetDateTimeSerializer extends InstantSerializerBase<OffsetDateTime>
{
public static final OffsetDateTimeSerializer INSTANCE = new OffsetDateTimeSerializer();

protected OffsetDateTimeSerializer() {
super(OffsetDateTime.class, dt -> dt.toInstant().toEpochMilli(),
OffsetDateTime::toEpochSecond, OffsetDateTime::getNano);
OffsetDateTime::toEpochSecond, OffsetDateTime::getNano,
dt -> DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(dt));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need the lambda here. You can replace:

dt -> DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(dt)

With:

DateTimeFormatter.ISO_OFFSET_DATE_TIME::format

(I'm pretty sure that's the syntax, but that's off the top of my head. Compiler and tests will confirm.)

}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.fasterxml.jackson.datatype.jsr310.ser;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class ZonedDateTimeSerializer extends InstantSerializerBase<ZonedDateTime>
{
public static final ZonedDateTimeSerializer INSTANCE = new ZonedDateTimeSerializer();

protected ZonedDateTimeSerializer() {
super(ZonedDateTime.class, dt -> dt.toInstant().toEpochMilli(),
ZonedDateTime::toEpochSecond, ZonedDateTime::getNano);
ZonedDateTime::toEpochSecond, ZonedDateTime::getNano,
// ISO_ZONED_DATE_TIME is not the ISO format, it is an extension of it
dt -> DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(dt));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the lambda here check for a setting to determine whether to use ISO_OFFSET_DATE_TIME or ISO_ZONED_DATE_TIME? Or does that have to come at a later date after changes are made to the Jackson core?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this PR only contained the ISO compatibility changes and introduced loss of time zone information. The idea of the SerializationFeature came up later.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import org.junit.Before;
import org.junit.Test;

import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;

import static org.junit.Assert.*;

public class TestInstantSerialization
{
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT;

private ObjectMapper mapper;

@Before
Expand Down Expand Up @@ -126,7 +130,7 @@ public void testSerializationAsString01() throws Exception
String value = this.mapper.writeValueAsString(date);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", '"' + date.toString() + '"', value);
assertEquals("The value is not correct.", '"' + FORMATTER.format(date) + '"', value);
}

@Test
Expand All @@ -138,7 +142,7 @@ public void testSerializationAsString02() throws Exception
String value = this.mapper.writeValueAsString(date);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", '"' + date.toString() + '"', value);
assertEquals("The value is not correct.", '"' + FORMATTER.format(date) + '"', value);
}

@Test
Expand All @@ -150,7 +154,7 @@ public void testSerializationAsString03() throws Exception
String value = this.mapper.writeValueAsString(date);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", '"' + date.toString() + '"', value);
assertEquals("The value is not correct.", '"' + FORMATTER.format(date) + '"', value);
}

@Test
Expand Down Expand Up @@ -192,7 +196,7 @@ public void testSerializationWithTypeInfo03() throws Exception

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.",
"[\"" + Instant.class.getName() + "\",\"" + date.toString() + "\"]", value);
"[\"" + Instant.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", value);
}

@Test
Expand Down Expand Up @@ -309,7 +313,7 @@ public void testDeserializationAsString01() throws Exception
{
Instant date = Instant.ofEpochSecond(0L);

Instant value = this.mapper.readValue('"' + date.toString() + '"', Instant.class);
Instant value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', Instant.class);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", date, value);
Expand All @@ -320,7 +324,7 @@ public void testDeserializationAsString02() throws Exception
{
Instant date = Instant.ofEpochSecond(123456789L, 183917322);

Instant value = this.mapper.readValue('"' + date.toString() + '"', Instant.class);
Instant value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', Instant.class);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", date, value);
Expand All @@ -331,7 +335,7 @@ public void testDeserializationAsString03() throws Exception
{
Instant date = Instant.now();

Instant value = this.mapper.readValue('"' + date.toString() + '"', Instant.class);
Instant value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', Instant.class);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", date, value);
Expand Down Expand Up @@ -391,7 +395,7 @@ public void testDeserializationWithTypeInfo04() throws Exception

this.mapper.addMixInAnnotations(Temporal.class, MockObjectConfiguration.class);
Temporal value = this.mapper.readValue(
"[\"" + Instant.class.getName() + "\",\"" + date.toString() + "\"]", Temporal.class
"[\"" + Instant.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class
);

assertNotNull("The value should not be null.", value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -27,6 +28,7 @@
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
Expand All @@ -36,6 +38,8 @@

public class TestOffsetDateTimeSerialization
{
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

private static final ZoneId Z1 = ZoneId.of("America/Chicago");

private static final ZoneId Z2 = ZoneId.of("America/Anchorage");
Expand Down Expand Up @@ -144,7 +148,7 @@ public void testSerializationAsString01() throws Exception
String value = this.mapper.writeValueAsString(date);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", '"' + date.toString() + '"', value);
assertEquals("The value is not correct.", '"' + FORMATTER.format(date) + '"', value);
}

@Test
Expand All @@ -156,7 +160,7 @@ public void testSerializationAsString02() throws Exception
String value = this.mapper.writeValueAsString(date);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", '"' + date.toString() + '"', value);
assertEquals("The value is not correct.", '"' + FORMATTER.format(date) + '"', value);
}

@Test
Expand All @@ -168,7 +172,7 @@ public void testSerializationAsString03() throws Exception
String value = this.mapper.writeValueAsString(date);

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.", '"' + date.toString() + '"', value);
assertEquals("The value is not correct.", '"' + FORMATTER.format(date) + '"', value);
}

@Test
Expand Down Expand Up @@ -212,7 +216,7 @@ public void testSerializationWithTypeInfo03() throws Exception

assertNotNull("The value should not be null.", value);
assertEquals("The value is not correct.",
"[\"" + OffsetDateTime.class.getName() + "\",\"" + date.toString() + "\"]", value);
"[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", value);
}

@Test
Expand Down Expand Up @@ -468,7 +472,7 @@ public void testDeserializationAsString01WithoutTimeZone() throws Exception
OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1);

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true);
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -482,7 +486,7 @@ public void testDeserializationAsString01WithTimeZone() throws Exception

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true);
this.mapper.setTimeZone(TimeZone.getDefault());
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -496,7 +500,7 @@ public void testDeserializationAsString01WithTimeZoneTurnedOff() throws Exceptio

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
this.mapper.setTimeZone(TimeZone.getDefault());
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -509,7 +513,7 @@ public void testDeserializationAsString02WithoutTimeZone() throws Exception
OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2);

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true);
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -523,7 +527,7 @@ public void testDeserializationAsString02WithTimeZone() throws Exception

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true);
this.mapper.setTimeZone(TimeZone.getDefault());
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -537,7 +541,7 @@ public void testDeserializationAsString02WithTimeZoneTurnedOff() throws Exceptio

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
this.mapper.setTimeZone(TimeZone.getDefault());
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -550,7 +554,7 @@ public void testDeserializationAsString03WithoutTimeZone() throws Exception
OffsetDateTime date = OffsetDateTime.now(Z3);

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true);
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -564,7 +568,7 @@ public void testDeserializationAsString03WithTimeZone() throws Exception

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true);
this.mapper.setTimeZone(TimeZone.getDefault());
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand All @@ -578,7 +582,7 @@ public void testDeserializationAsString03WithTimeZoneTurnedOff() throws Exceptio

this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
this.mapper.setTimeZone(TimeZone.getDefault());
OffsetDateTime value = this.mapper.readValue('"' + date.toString() + '"', OffsetDateTime.class);
OffsetDateTime value = this.mapper.readValue('"' + FORMATTER.format(date) + '"', OffsetDateTime.class);

assertNotNull("The value should not be null.", value);
assertIsEqual(date, value);
Expand Down Expand Up @@ -696,7 +700,7 @@ public void testDeserializationWithTypeInfo04WithoutTimeZone() throws Exception
this.mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, true);
this.mapper.addMixInAnnotations(Temporal.class, MockObjectConfiguration.class);
Temporal value = this.mapper.readValue(
"[\"" + OffsetDateTime.class.getName() + "\",\"" + date.toString() + "\"]", Temporal.class
"[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class
);

assertNotNull("The value should not be null.", value);
Expand All @@ -714,7 +718,7 @@ public void testDeserializationWithTypeInfo04WithTimeZone() throws Exception
this.mapper.setTimeZone(TimeZone.getDefault());
this.mapper.addMixInAnnotations(Temporal.class, MockObjectConfiguration.class);
Temporal value = this.mapper.readValue(
"[\"" + OffsetDateTime.class.getName() + "\",\"" + date.toString() + "\"]", Temporal.class
"[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class
);

assertNotNull("The value should not be null.", value);
Expand All @@ -732,7 +736,7 @@ public void testDeserializationWithTypeInfo04WithTimeZoneTurnedOff() throws Exce
this.mapper.setTimeZone(TimeZone.getDefault());
this.mapper.addMixInAnnotations(Temporal.class, MockObjectConfiguration.class);
Temporal value = this.mapper.readValue(
"[\"" + OffsetDateTime.class.getName() + "\",\"" + date.toString() + "\"]", Temporal.class
"[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class
);

assertNotNull("The value should not be null.", value);
Expand Down
Loading