-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
[Java][okhttp-gson] Fix LocalDateTime Serialization
#24643
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
Changes from all commits
1d2091f
88c49c5
79a3395
5c08dfe
df00cb6
9451fcd
a9fe3b0
33cb5ca
7629bd8
7c49e51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,8 +36,10 @@ | |
| import java.text.ParseException; | ||
| import java.text.ParsePosition; | ||
| import java.time.LocalDate; | ||
| import java.time.LocalDateTime; | ||
| import java.time.OffsetDateTime; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.time.format.DateTimeParseException; | ||
| import java.util.Date; | ||
| import java.util.Locale; | ||
| import java.util.Map; | ||
|
|
@@ -56,6 +58,7 @@ public class JSON { | |
| private static SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); | ||
| private static OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); | ||
| private static LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); | ||
| private static LocalDateTimeTypeAdapter localDateTimeTypeAdapter = new LocalDateTimeTypeAdapter(); | ||
| private static ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
|
|
@@ -95,6 +98,7 @@ private static Class getClassByDiscriminator(Map classByDiscriminatorValue, Stri | |
| gsonBuilder.registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter); | ||
| gsonBuilder.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter); | ||
| gsonBuilder.registerTypeAdapter(LocalDate.class, localDateTypeAdapter); | ||
| gsonBuilder.registerTypeAdapter(LocalDateTime.class, localDateTimeTypeAdapter); | ||
| gsonBuilder.registerTypeAdapter(byte[].class, byteArrayAdapter); | ||
| gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Category.CustomTypeAdapterFactory()); | ||
| gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.ModelApiResponse.CustomTypeAdapterFactory()); | ||
|
|
@@ -302,6 +306,55 @@ public LocalDate read(JsonReader in) throws IOException { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Gson TypeAdapter for JSR310 LocalDateTime type | ||
| */ | ||
| public static class LocalDateTimeTypeAdapter extends TypeAdapter<LocalDateTime> { | ||
|
|
||
| private DateTimeFormatter formatter; | ||
|
|
||
| public LocalDateTimeTypeAdapter() { | ||
| this(DateTimeFormatter.ISO_LOCAL_DATE_TIME); | ||
| } | ||
|
|
||
| public LocalDateTimeTypeAdapter(DateTimeFormatter formatter) { | ||
| this.formatter = formatter; | ||
| } | ||
|
|
||
| public void setFormat(DateTimeFormatter dateFormat) { | ||
| this.formatter = dateFormat; | ||
| } | ||
|
|
||
| @Override | ||
| public void write(JsonWriter out, LocalDateTime date) throws IOException { | ||
| if (date == null) { | ||
| out.nullValue(); | ||
| } else { | ||
| out.value(formatter.format(date)); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public LocalDateTime read(JsonReader in) throws IOException { | ||
| switch (in.peek()) { | ||
| case NULL: | ||
| in.nextNull(); | ||
| return null; | ||
| default: | ||
| String date = in.nextString(); | ||
| try { | ||
| return LocalDateTime.parse(date, formatter); | ||
| } catch (DateTimeParseException e) { | ||
| if (date.length() > 10 && date.charAt(10) == ' ') { | ||
| date = date.substring(0, 10) + 'T' + date.substring(11); | ||
| return LocalDateTime.parse(date, formatter); | ||
| } | ||
| throw e; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public static void setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { | ||
| offsetDateTimeTypeAdapter.setFormat(dateFormat); | ||
| } | ||
|
|
@@ -310,6 +363,10 @@ public static void setLocalDateFormat(DateTimeFormatter dateFormat) { | |
| localDateTypeAdapter.setFormat(dateFormat); | ||
| } | ||
|
|
||
| public static void setLocalDateTimeFormat(DateTimeFormatter dateFormat) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Generated clients cannot configure the new LocalDateTime formatter through Prompt for AI agents |
||
| localDateTimeTypeAdapter.setFormat(dateFormat); | ||
| } | ||
|
|
||
| /** | ||
| * Gson TypeAdapter for java.sql.Date type | ||
| * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: For the space-separated format this PR is adding,
read()now treats a parse failure as the expected path: every space-separatedLocalDateTimemust first throw and catch aDateTimeParseException, then reparse after rewriting the space to 'T'. A server that consistently emits RFC 3339 space-separated datetimes (e.g.2016-09-09 08:02:03) will pay the cost of constructing and unwinding an exception for each and every field, rather than on rare error cases. The typical 'T'-separated path is the fast path, so impact is limited, but it may be worth normalizing the separator up front (e.g. checkingcharAt(10)before the first parse) while still only doing so when the formatter can accept it, to avoid exception-based control flow on the supported input format.Prompt for AI agents