-
-
Notifications
You must be signed in to change notification settings - Fork 143
[Avro] Add logicalType
support for some java.time
types; add AvroJavaTimeModule
for native ser/deser
#283
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
Merged
cowtowncoder
merged 9 commits into
FasterXML:2.13
from
MichalFoksa:feature/2.13/avro/logicalType_support_for_date_time_types
Jun 29, 2021
Merged
[Avro] Add logicalType
support for some java.time
types; add AvroJavaTimeModule
for native ser/deser
#283
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
62570b4
logicalType support for few date time types.
MichalFoksa 1bd02ba
Support to serialize and de-serialize java.time types into Avro type …
MichalFoksa 8f52e52
Base deserializer and try-catch block removed, no need to try to wrap…
MichalFoksa ba76375
Documentation added and corrected.
MichalFoksa 956f365
Use addModule() instead of addModules()
MichalFoksa 170525f
Use
MichalFoksa e02185d
Better explanation
MichalFoksa 707f3a4
Bit easier to understand code
MichalFoksa a05b4ab
More notes on AvroJavaTimeModule.
MichalFoksa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
avro/src/main/java/com/fasterxml/jackson/dataformat/avro/jsr310/AvroJavaTimeModule.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.fasterxml.jackson.dataformat.avro.jsr310; | ||
|
||
import com.fasterxml.jackson.databind.module.SimpleModule; | ||
import com.fasterxml.jackson.dataformat.avro.PackageVersion; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.deser.AvroInstantDeserializer; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.deser.AvroLocalDateDeserializer; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.deser.AvroLocalDateTimeDeserializer; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.deser.AvroLocalTimeDeserializer; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.ser.AvroInstantSerializer; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.ser.AvroLocalDateSerializer; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.ser.AvroLocalDateTimeSerializer; | ||
import com.fasterxml.jackson.dataformat.avro.jsr310.ser.AvroLocalTimeSerializer; | ||
|
||
import java.time.Instant; | ||
import java.time.LocalDate; | ||
import java.time.LocalDateTime; | ||
import java.time.LocalTime; | ||
import java.time.OffsetDateTime; | ||
import java.time.ZonedDateTime; | ||
|
||
/** | ||
* A module that installs a collection of serializers and deserializers for java.time classes. | ||
* | ||
* This module is to be used either: | ||
* - Instead of Java 8 date/time module (com.fasterxml.jackson.datatype.jsr310.JavaTimeModule) or | ||
* - to override Java 8 date/time module and for that, module must be registered AFTER Java 8 date/time module. | ||
*/ | ||
public class AvroJavaTimeModule extends SimpleModule { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public AvroJavaTimeModule() { | ||
super(AvroJavaTimeModule.class.getName(), PackageVersion.VERSION); | ||
|
||
addSerializer(Instant.class, AvroInstantSerializer.INSTANT); | ||
addSerializer(OffsetDateTime.class, AvroInstantSerializer.OFFSET_DATE_TIME); | ||
addSerializer(ZonedDateTime.class, AvroInstantSerializer.ZONED_DATE_TIME); | ||
addSerializer(LocalDateTime.class, AvroLocalDateTimeSerializer.INSTANCE); | ||
addSerializer(LocalDate.class, AvroLocalDateSerializer.INSTANCE); | ||
addSerializer(LocalTime.class, AvroLocalTimeSerializer.INSTANCE); | ||
|
||
addDeserializer(Instant.class, AvroInstantDeserializer.INSTANT); | ||
addDeserializer(OffsetDateTime.class, AvroInstantDeserializer.OFFSET_DATE_TIME); | ||
addDeserializer(ZonedDateTime.class, AvroInstantDeserializer.ZONED_DATE_TIME); | ||
addDeserializer(LocalDateTime.class, AvroLocalDateTimeDeserializer.INSTANCE); | ||
addDeserializer(LocalDate.class, AvroLocalDateDeserializer.INSTANCE); | ||
addDeserializer(LocalTime.class, AvroLocalTimeDeserializer.INSTANCE); | ||
} | ||
|
||
} |
50 changes: 50 additions & 0 deletions
50
...main/java/com/fasterxml/jackson/dataformat/avro/jsr310/deser/AvroInstantDeserializer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.fasterxml.jackson.dataformat.avro.jsr310.deser; | ||
|
||
import java.time.Instant; | ||
import java.time.OffsetDateTime; | ||
import java.time.ZoneId; | ||
import java.time.ZonedDateTime; | ||
import java.time.temporal.Temporal; | ||
import java.util.function.BiFunction; | ||
|
||
/** | ||
* Deserializer for variants of java.time classes (Instant, OffsetDateTime, ZonedDateTime) from an integer value. | ||
* | ||
* Deserialized value represents an instant on the global timeline, independent of a particular time zone or | ||
* calendar, with a precision of one millisecond from the unix epoch, 1 January 1970 00:00:00.000 UTC. | ||
* Time zone information is lost at serialization. Time zone data types receives time zone from deserialization context. | ||
* | ||
* Deserialization from string is not supported. | ||
* | ||
* @param <T> The type of a instant class that can be deserialized. | ||
*/ | ||
public class AvroInstantDeserializer<T extends Temporal> extends AvroJavaTimeDeserializerBase <T> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public static final AvroInstantDeserializer<Instant> INSTANT = | ||
new AvroInstantDeserializer<>(Instant.class, (instant, zoneID) -> instant); | ||
|
||
public static final AvroInstantDeserializer<OffsetDateTime> OFFSET_DATE_TIME = | ||
new AvroInstantDeserializer<>(OffsetDateTime.class, OffsetDateTime::ofInstant); | ||
|
||
public static final AvroInstantDeserializer<ZonedDateTime> ZONED_DATE_TIME = | ||
new AvroInstantDeserializer<>(ZonedDateTime.class, ZonedDateTime::ofInstant); | ||
|
||
protected final BiFunction<Instant, ZoneId, T> fromInstant; | ||
|
||
protected AvroInstantDeserializer(Class<T> supportedType, BiFunction<Instant, ZoneId, T> fromInstant) { | ||
super(supportedType); | ||
this.fromInstant = fromInstant; | ||
} | ||
|
||
@Override | ||
protected T fromLong(long longValue, ZoneId defaultZoneId) { | ||
/** | ||
* Number of milliseconds, independent of a particular time zone or calendar, | ||
* from 1 January 1970 00:00:00.000 UTC. | ||
*/ | ||
return fromInstant.apply(Instant.ofEpochMilli(longValue), defaultZoneId); | ||
} | ||
|
||
} |
36 changes: 36 additions & 0 deletions
36
...java/com/fasterxml/jackson/dataformat/avro/jsr310/deser/AvroJavaTimeDeserializerBase.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.fasterxml.jackson.dataformat.avro.jsr310.deser; | ||
|
||
import com.fasterxml.jackson.core.JsonParser; | ||
import com.fasterxml.jackson.databind.DeserializationContext; | ||
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; | ||
import com.fasterxml.jackson.databind.type.LogicalType; | ||
|
||
import java.io.IOException; | ||
import java.time.ZoneId; | ||
|
||
import static com.fasterxml.jackson.core.JsonToken.VALUE_NUMBER_INT; | ||
|
||
public abstract class AvroJavaTimeDeserializerBase<T> extends StdScalarDeserializer<T> { | ||
|
||
protected AvroJavaTimeDeserializerBase(Class<T> supportedType) { | ||
super(supportedType); | ||
} | ||
|
||
@Override | ||
public LogicalType logicalType() { | ||
return LogicalType.DateTime; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public T deserialize(JsonParser p, DeserializationContext context) throws IOException { | ||
if (p.getCurrentToken() == VALUE_NUMBER_INT) { | ||
final ZoneId defaultZoneId = context.getTimeZone().toZoneId().normalized(); | ||
return fromLong(p.getLongValue(), defaultZoneId); | ||
} else { | ||
return (T) context.handleUnexpectedToken(_valueClass, p); | ||
} | ||
} | ||
|
||
protected abstract T fromLong(long longValue, ZoneId defaultZoneId); | ||
} |
31 changes: 31 additions & 0 deletions
31
...in/java/com/fasterxml/jackson/dataformat/avro/jsr310/deser/AvroLocalDateDeserializer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.fasterxml.jackson.dataformat.avro.jsr310.deser; | ||
|
||
import java.time.LocalDate; | ||
import java.time.ZoneId; | ||
|
||
/** | ||
* Deserializer for {@link LocalDate} from and integer value. | ||
* | ||
* Deserialized value represents number of days from the unix epoch, 1 January 1970. | ||
* | ||
* Deserialization from string is not supported. | ||
*/ | ||
public class AvroLocalDateDeserializer extends AvroJavaTimeDeserializerBase<LocalDate> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public static final AvroLocalDateDeserializer INSTANCE = new AvroLocalDateDeserializer(); | ||
|
||
protected AvroLocalDateDeserializer() { | ||
super(LocalDate.class); | ||
} | ||
|
||
@Override | ||
protected LocalDate fromLong(long longValue, ZoneId defaultZoneId) { | ||
/** | ||
* Number of days from the unix epoch, 1 January 1970.. | ||
*/ | ||
return LocalDate.ofEpochDay(longValue); | ||
} | ||
|
||
} |
35 changes: 35 additions & 0 deletions
35
...ava/com/fasterxml/jackson/dataformat/avro/jsr310/deser/AvroLocalDateTimeDeserializer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.fasterxml.jackson.dataformat.avro.jsr310.deser; | ||
|
||
import java.time.Instant; | ||
import java.time.LocalDateTime; | ||
import java.time.ZoneId; | ||
import java.time.ZoneOffset; | ||
|
||
/** | ||
* Deserializer for {@link LocalDateTime} from an integer value. | ||
* | ||
* Deserialized value represents timestamp in a local timezone, regardless of what specific time zone | ||
* is considered local, with a precision of one millisecond from 1 January 1970 00:00:00.000. | ||
* | ||
* Deserialization from string is not supported. | ||
*/ | ||
public class AvroLocalDateTimeDeserializer extends AvroJavaTimeDeserializerBase<LocalDateTime> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public static final AvroLocalDateTimeDeserializer INSTANCE = new AvroLocalDateTimeDeserializer(); | ||
|
||
protected AvroLocalDateTimeDeserializer() { | ||
super(LocalDateTime.class); | ||
} | ||
|
||
@Override | ||
protected LocalDateTime fromLong(long longValue, ZoneId defaultZoneId) { | ||
/** | ||
* Number of milliseconds in a local timezone, regardless of what specific time zone is considered local, | ||
* from 1 January 1970 00:00:00.000. | ||
*/ | ||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(longValue), ZoneOffset.ofTotalSeconds(0)); | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
...in/java/com/fasterxml/jackson/dataformat/avro/jsr310/deser/AvroLocalTimeDeserializer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.fasterxml.jackson.dataformat.avro.jsr310.deser; | ||
|
||
import java.time.LocalTime; | ||
import java.time.ZoneId; | ||
|
||
/** | ||
* Deserializer for {@link LocalTime} from an integer value. | ||
* | ||
* Deserialized value represents time of day, with no reference to a particular calendar, | ||
* time zone or date, where the int stores the number of milliseconds after midnight, 00:00:00.000. | ||
* | ||
* Deserialization from string is not supported. | ||
*/ | ||
public class AvroLocalTimeDeserializer extends AvroJavaTimeDeserializerBase<LocalTime> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public static final AvroLocalTimeDeserializer INSTANCE = new AvroLocalTimeDeserializer(); | ||
|
||
protected AvroLocalTimeDeserializer() { | ||
super(LocalTime.class); | ||
} | ||
|
||
@Override | ||
protected LocalTime fromLong(long longValue, ZoneId defaultZoneId) { | ||
/** | ||
* Number of milliseconds, with no reference to a particular calendar, time zone or date, after | ||
* midnight, 00:00:00.000. | ||
*/ | ||
return LocalTime.ofNanoOfDay(longValue * 1000_000L); | ||
} | ||
|
||
} |
76 changes: 76 additions & 0 deletions
76
...src/main/java/com/fasterxml/jackson/dataformat/avro/jsr310/ser/AvroInstantSerializer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.fasterxml.jackson.dataformat.avro.jsr310.ser; | ||
|
||
import com.fasterxml.jackson.core.JsonGenerator; | ||
import com.fasterxml.jackson.core.JsonParser; | ||
import com.fasterxml.jackson.databind.JavaType; | ||
import com.fasterxml.jackson.databind.JsonMappingException; | ||
import com.fasterxml.jackson.databind.SerializerProvider; | ||
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; | ||
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; | ||
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; | ||
|
||
import java.io.IOException; | ||
import java.time.Instant; | ||
import java.time.OffsetDateTime; | ||
import java.time.ZonedDateTime; | ||
import java.time.temporal.Temporal; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* Serializer for variants of java.time classes (Instant, OffsetDateTime, ZonedDateTime) into long value. | ||
* | ||
* Serialized value represents an instant on the global timeline, independent of a particular time zone or | ||
* calendar, with a precision of one millisecond from the unix epoch, 1 January 1970 00:00:00.000 UTC. | ||
* Please note that time zone information gets lost in this process. Upon reading a value back, we can only | ||
* reconstruct the instant, but not the original representation. | ||
* | ||
* Note: In combination with {@link com.fasterxml.jackson.dataformat.avro.schema.DateTimeVisitor} it aims to produce | ||
* Avro schema with type long and logicalType timestamp-millis: | ||
* { | ||
* "type" : "long", | ||
* "logicalType" : "timestamp-millis" | ||
* } | ||
* | ||
* {@link AvroInstantSerializer} does not support serialization to string. | ||
* | ||
* @param <T> The type of a instant class that can be serialized. | ||
*/ | ||
public class AvroInstantSerializer<T extends Temporal> extends StdScalarSerializer<T> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public static final AvroInstantSerializer<Instant> INSTANT = | ||
new AvroInstantSerializer<>(Instant.class, Function.identity()); | ||
|
||
public static final AvroInstantSerializer<OffsetDateTime> OFFSET_DATE_TIME = | ||
new AvroInstantSerializer<>(OffsetDateTime.class, OffsetDateTime::toInstant); | ||
|
||
public static final AvroInstantSerializer<ZonedDateTime> ZONED_DATE_TIME = | ||
new AvroInstantSerializer<>(ZonedDateTime.class, ZonedDateTime::toInstant); | ||
|
||
private final Function<T, Instant> getInstant; | ||
|
||
protected AvroInstantSerializer(Class<T> t, Function<T, Instant> getInstant) { | ||
super(t); | ||
this.getInstant = getInstant; | ||
} | ||
|
||
@Override | ||
public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException { | ||
/** | ||
* Number of milliseconds, independent of a particular time zone or calendar, | ||
* from 1 January 1970 00:00:00.000 UTC. | ||
*/ | ||
final Instant instant = getInstant.apply(value); | ||
gen.writeNumber(instant.toEpochMilli()); | ||
} | ||
|
||
@Override | ||
cowtowncoder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { | ||
JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint); | ||
if (v2 != null) { | ||
v2.numberType(JsonParser.NumberType.LONG); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.