Skip to content

Commit 462e7d4

Browse files
committed
[#19921] Add RFC 3339 compatible Jackson module for java.time types
1 parent 654f62c commit 462e7d4

File tree

173 files changed

+6524
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

173 files changed

+6524
-5
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java

+2
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,8 @@ public void processOpts() {
678678
additionalProperties.remove(SERIALIZATION_LIBRARY_GSON);
679679
additionalProperties.remove(SERIALIZATION_LIBRARY_JSONB);
680680
supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", invokerFolder, "RFC3339DateFormat.java"));
681+
supportingFiles.add(new SupportingFile("RFC3339InstantDeserializer.mustache", invokerFolder, "RFC3339InstantDeserializer.java"));
682+
supportingFiles.add(new SupportingFile("RFC3339JavaTimeModule.mustache", invokerFolder, "RFC3339JavaTimeModule.java"));
681683
break;
682684
case SERIALIZATION_LIBRARY_GSON:
683685
additionalProperties.put(SERIALIZATION_LIBRARY_GSON, "true");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{{>licenseInfo}}
2+
package {{invokerPackage}};
3+
4+
import java.io.IOException;
5+
import java.time.Instant;
6+
import java.time.OffsetDateTime;
7+
import java.time.ZoneId;
8+
import java.time.ZonedDateTime;
9+
import java.time.format.DateTimeFormatter;
10+
import java.time.temporal.Temporal;
11+
import java.time.temporal.TemporalAccessor;
12+
import java.util.function.BiFunction;
13+
import java.util.function.Function;
14+
15+
import com.fasterxml.jackson.core.JsonParser;
16+
import com.fasterxml.jackson.databind.DeserializationContext;
17+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
18+
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
19+
20+
{{>generatedAnnotation}}
21+
public class RFC3339InstantDeserializer<T extends Temporal> extends InstantDeserializer<T> {
22+
23+
private final static boolean DEFAULT_NORMALIZE_ZONE_ID = JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault();
24+
private final static boolean DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
25+
= JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault();
26+
27+
public static final RFC3339InstantDeserializer<Instant> INSTANT = new RFC3339InstantDeserializer<>(
28+
Instant.class, DateTimeFormatter.ISO_INSTANT,
29+
Instant::from,
30+
a -> Instant.ofEpochMilli( a.value ),
31+
a -> Instant.ofEpochSecond( a.integer, a.fraction ),
32+
null,
33+
true, // yes, replace zero offset with Z
34+
DEFAULT_NORMALIZE_ZONE_ID,
35+
DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
36+
);
37+
38+
public static final RFC3339InstantDeserializer<OffsetDateTime> OFFSET_DATE_TIME = new RFC3339InstantDeserializer<>(
39+
OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME,
40+
OffsetDateTime::from,
41+
a -> OffsetDateTime.ofInstant( Instant.ofEpochMilli( a.value ), a.zoneId ),
42+
a -> OffsetDateTime.ofInstant( Instant.ofEpochSecond( a.integer, a.fraction ), a.zoneId ),
43+
(d, z) -> ( d.isEqual( OffsetDateTime.MIN ) || d.isEqual( OffsetDateTime.MAX ) ?
44+
d :
45+
d.withOffsetSameInstant( z.getRules().getOffset( d.toLocalDateTime() ) ) ),
46+
true, // yes, replace zero offset with Z
47+
DEFAULT_NORMALIZE_ZONE_ID,
48+
DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
49+
);
50+
51+
public static final RFC3339InstantDeserializer<ZonedDateTime> ZONED_DATE_TIME = new RFC3339InstantDeserializer<>(
52+
ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME,
53+
ZonedDateTime::from,
54+
a -> ZonedDateTime.ofInstant( Instant.ofEpochMilli( a.value ), a.zoneId ),
55+
a -> ZonedDateTime.ofInstant( Instant.ofEpochSecond( a.integer, a.fraction ), a.zoneId ),
56+
ZonedDateTime::withZoneSameInstant,
57+
false, // keep zero offset and Z separate since zones explicitly supported
58+
DEFAULT_NORMALIZE_ZONE_ID,
59+
DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
60+
);
61+
62+
protected RFC3339InstantDeserializer(
63+
Class<T> supportedType,
64+
DateTimeFormatter formatter,
65+
Function<TemporalAccessor, T> parsedToValue,
66+
Function<FromIntegerArguments, T> fromMilliseconds,
67+
Function<FromDecimalArguments, T> fromNanoseconds,
68+
BiFunction<T, ZoneId, T> adjust,
69+
boolean replaceZeroOffsetAsZ,
70+
boolean normalizeZoneId,
71+
boolean readNumericStringsAsTimestamp) {
72+
super(
73+
supportedType,
74+
formatter,
75+
parsedToValue,
76+
fromMilliseconds,
77+
fromNanoseconds,
78+
adjust,
79+
replaceZeroOffsetAsZ,
80+
normalizeZoneId,
81+
readNumericStringsAsTimestamp
82+
);
83+
}
84+
85+
@Override
86+
protected T _fromString(JsonParser p, DeserializationContext ctxt, String string0) throws IOException {
87+
return super._fromString(p, ctxt, string0.replace( ' ', 'T' ));
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{{>licenseInfo}}
2+
package {{invokerPackage}};
3+
4+
import java.time.Instant;
5+
import java.time.OffsetDateTime;
6+
import java.time.ZonedDateTime;
7+
8+
import com.fasterxml.jackson.databind.module.SimpleModule;
9+
10+
{{>generatedAnnotation}}
11+
public class RFC3339JavaTimeModule extends SimpleModule {
12+
13+
public RFC3339JavaTimeModule() {
14+
super("RFC3339JavaTimeModule");
15+
16+
addDeserializer(Instant.class, RFC3339InstantDeserializer.INSTANT);
17+
addDeserializer(OffsetDateTime.class, RFC3339InstantDeserializer.OFFSET_DATE_TIME);
18+
addDeserializer(ZonedDateTime.class, RFC3339InstantDeserializer.ZONED_DATE_TIME);
19+
}
20+
}

modules/openapi-generator/src/main/resources/Java/libraries/apache-httpclient/ApiClient.mustache

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
149149
{{#openApiNullable}}
150150
objectMapper.registerModule(new JsonNullableModule());
151151
{{/openApiNullable}}
152+
objectMapper.registerModule(new RFC3339JavaTimeModule());
152153
objectMapper.setDateFormat(ApiClient.buildDefaultDateFormat());
153154

154155
dateFormat = ApiClient.buildDefaultDateFormat();

modules/openapi-generator/src/main/resources/Java/libraries/feign/ApiClient.mustache

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ public class ApiClient {
186186
objectMapper.registerModule(new JodaModule());
187187
{{/joda}}
188188
objectMapper.registerModule(new JavaTimeModule());
189+
objectMapper.registerModule(new RFC3339JavaTimeModule());
189190
{{#openApiNullable}}
190191
JsonNullableModule jnm = new JsonNullableModule();
191192
objectMapper.registerModule(jnm);

modules/openapi-generator/src/main/resources/Java/libraries/jersey2/JSON.mustache

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class JSON implements ContextResolver<ObjectMapper> {
4242
{{#openApiNullable}}
4343
.addModule(new JsonNullableModule())
4444
{{/openApiNullable}}
45+
.addModule(new RFC3339JavaTimeModule())
4546
.build();
4647
}
4748

modules/openapi-generator/src/main/resources/Java/libraries/jersey3/JSON.mustache

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class JSON implements ContextResolver<ObjectMapper> {
4242
{{#openApiNullable}}
4343
.addModule(new JsonNullableModule())
4444
{{/openApiNullable}}
45+
.addModule(new RFC3339JavaTimeModule())
4546
.build();
4647
}
4748

modules/openapi-generator/src/main/resources/Java/libraries/native/ApiClient.mustache

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ public class ApiClient {
196196
{{#openApiNullable}}
197197
mapper.registerModule(new JsonNullableModule());
198198
{{/openApiNullable}}
199+
mapper.registerModule(new RFC3339JavaTimeModule());
199200
return mapper;
200201
}
201202

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ public void testJdkHttpClient() {
536536

537537
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
538538

539-
assertThat(files).hasSize(32);
539+
assertThat(files).hasSize(34);
540540
validateJavaSourceFiles(files);
541541
assertThat(output.resolve("src/main/java/xyz/abcdef/api/DefaultApi.java")).content().contains(
542542
"public class DefaultApi",
@@ -606,7 +606,7 @@ public void testJdkHttpAsyncClient() {
606606

607607
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
608608

609-
assertThat(files).hasSize(35);
609+
assertThat(files).hasSize(37);
610610

611611
validateJavaSourceFiles(files);
612612
assertThat(output.resolve("src/main/java/xyz/abcdef/api/PingApi.java")).content().contains(
@@ -1427,7 +1427,7 @@ public void testNativeClientWhiteSpacePathParamEncoding() {
14271427
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
14281428

14291429
validateJavaSourceFiles(files);
1430-
assertThat(files).hasSize(35);
1430+
assertThat(files).hasSize(37);
14311431
TestUtils.assertFileContains(output.resolve("src/main/java/xyz/abcdef/ApiClient.java"),
14321432
"public static String urlEncode(String s) { return URLEncoder.encode(s,"
14331433
+ " UTF_8).replaceAll(\"\\\\+\", \"%20\"); }"
@@ -1450,7 +1450,7 @@ public void testNativeClientExplodedQueryParamObject() {
14501450
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
14511451

14521452
validateJavaSourceFiles(files);
1453-
assertThat(files).hasSize(38);
1453+
assertThat(files).hasSize(40);
14541454
assertThat(output.resolve("src/main/java/xyz/abcdef/api/DefaultApi.java")).content()
14551455
.contains(
14561456
"localVarQueryParams.addAll(ApiClient.parameterToPairs(\"since\", queryObject.getSince()));",

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/apachehttpclient/ApacheHttpClientCodegenTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void testApacheHttpClientExplodedQueryParamObject() throws IOException {
5656
DefaultGenerator generator = new DefaultGenerator();
5757
List<File> files = generator.opts(clientOptInput).generate();
5858

59-
Assert.assertEquals(files.size(), 42);
59+
Assert.assertEquals(files.size(), 44);
6060
validateJavaSourceFiles(files);
6161

6262
TestUtils.assertFileContains(Paths.get(output + "/src/main/java/xyz/abcdef/api/DefaultApi.java"),

samples/client/echo_api/java/apache-httpclient/.openapi-generator/FILES

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ src/main/java/org/openapitools/client/Configuration.java
3939
src/main/java/org/openapitools/client/JavaTimeFormatter.java
4040
src/main/java/org/openapitools/client/Pair.java
4141
src/main/java/org/openapitools/client/RFC3339DateFormat.java
42+
src/main/java/org/openapitools/client/RFC3339InstantDeserializer.java
43+
src/main/java/org/openapitools/client/RFC3339JavaTimeModule.java
4244
src/main/java/org/openapitools/client/ServerConfiguration.java
4345
src/main/java/org/openapitools/client/ServerVariable.java
4446
src/main/java/org/openapitools/client/StringUtil.java

samples/client/echo_api/java/apache-httpclient/src/main/java/org/openapitools/client/ApiClient.java

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ public ApiClient(CloseableHttpClient httpClient) {
122122
objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
123123
objectMapper.registerModule(new JavaTimeModule());
124124
objectMapper.registerModule(new JsonNullableModule());
125+
objectMapper.registerModule(new RFC3339JavaTimeModule());
125126
objectMapper.setDateFormat(ApiClient.buildDefaultDateFormat());
126127

127128
dateFormat = ApiClient.buildDefaultDateFormat();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Echo Server API
3+
* Echo Server API
4+
*
5+
* The version of the OpenAPI document: 0.1.0
6+
* Contact: [email protected]
7+
*
8+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
9+
* https://openapi-generator.tech
10+
* Do not edit the class manually.
11+
*/
12+
13+
package org.openapitools.client;
14+
15+
import java.io.IOException;
16+
import java.time.Instant;
17+
import java.time.OffsetDateTime;
18+
import java.time.ZoneId;
19+
import java.time.ZonedDateTime;
20+
import java.time.format.DateTimeFormatter;
21+
import java.time.temporal.Temporal;
22+
import java.time.temporal.TemporalAccessor;
23+
import java.util.function.BiFunction;
24+
import java.util.function.Function;
25+
26+
import com.fasterxml.jackson.core.JsonParser;
27+
import com.fasterxml.jackson.databind.DeserializationContext;
28+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
29+
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
30+
31+
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.10.0-SNAPSHOT")
32+
public class RFC3339InstantDeserializer<T extends Temporal> extends InstantDeserializer<T> {
33+
34+
private final static boolean DEFAULT_NORMALIZE_ZONE_ID = JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault();
35+
private final static boolean DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
36+
= JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault();
37+
38+
public static final RFC3339InstantDeserializer<Instant> INSTANT = new RFC3339InstantDeserializer<>(
39+
Instant.class, DateTimeFormatter.ISO_INSTANT,
40+
Instant::from,
41+
a -> Instant.ofEpochMilli( a.value ),
42+
a -> Instant.ofEpochSecond( a.integer, a.fraction ),
43+
null,
44+
true, // yes, replace zero offset with Z
45+
DEFAULT_NORMALIZE_ZONE_ID,
46+
DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
47+
);
48+
49+
public static final RFC3339InstantDeserializer<OffsetDateTime> OFFSET_DATE_TIME = new RFC3339InstantDeserializer<>(
50+
OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME,
51+
OffsetDateTime::from,
52+
a -> OffsetDateTime.ofInstant( Instant.ofEpochMilli( a.value ), a.zoneId ),
53+
a -> OffsetDateTime.ofInstant( Instant.ofEpochSecond( a.integer, a.fraction ), a.zoneId ),
54+
(d, z) -> ( d.isEqual( OffsetDateTime.MIN ) || d.isEqual( OffsetDateTime.MAX ) ?
55+
d :
56+
d.withOffsetSameInstant( z.getRules().getOffset( d.toLocalDateTime() ) ) ),
57+
true, // yes, replace zero offset with Z
58+
DEFAULT_NORMALIZE_ZONE_ID,
59+
DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
60+
);
61+
62+
public static final RFC3339InstantDeserializer<ZonedDateTime> ZONED_DATE_TIME = new RFC3339InstantDeserializer<>(
63+
ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME,
64+
ZonedDateTime::from,
65+
a -> ZonedDateTime.ofInstant( Instant.ofEpochMilli( a.value ), a.zoneId ),
66+
a -> ZonedDateTime.ofInstant( Instant.ofEpochSecond( a.integer, a.fraction ), a.zoneId ),
67+
ZonedDateTime::withZoneSameInstant,
68+
false, // keep zero offset and Z separate since zones explicitly supported
69+
DEFAULT_NORMALIZE_ZONE_ID,
70+
DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
71+
);
72+
73+
protected RFC3339InstantDeserializer(
74+
Class<T> supportedType,
75+
DateTimeFormatter formatter,
76+
Function<TemporalAccessor, T> parsedToValue,
77+
Function<FromIntegerArguments, T> fromMilliseconds,
78+
Function<FromDecimalArguments, T> fromNanoseconds,
79+
BiFunction<T, ZoneId, T> adjust,
80+
boolean replaceZeroOffsetAsZ,
81+
boolean normalizeZoneId,
82+
boolean readNumericStringsAsTimestamp) {
83+
super(
84+
supportedType,
85+
formatter,
86+
parsedToValue,
87+
fromMilliseconds,
88+
fromNanoseconds,
89+
adjust,
90+
replaceZeroOffsetAsZ,
91+
normalizeZoneId,
92+
readNumericStringsAsTimestamp
93+
);
94+
}
95+
96+
@Override
97+
protected T _fromString(JsonParser p, DeserializationContext ctxt, String string0) throws IOException {
98+
return super._fromString(p, ctxt, string0.replace( ' ', 'T' ));
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Echo Server API
3+
* Echo Server API
4+
*
5+
* The version of the OpenAPI document: 0.1.0
6+
* Contact: [email protected]
7+
*
8+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
9+
* https://openapi-generator.tech
10+
* Do not edit the class manually.
11+
*/
12+
13+
package org.openapitools.client;
14+
15+
import java.time.Instant;
16+
import java.time.OffsetDateTime;
17+
import java.time.ZonedDateTime;
18+
19+
import com.fasterxml.jackson.databind.module.SimpleModule;
20+
21+
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.10.0-SNAPSHOT")
22+
public class RFC3339JavaTimeModule extends SimpleModule {
23+
24+
public RFC3339JavaTimeModule() {
25+
super("RFC3339JavaTimeModule");
26+
27+
addDeserializer(Instant.class, RFC3339InstantDeserializer.INSTANT);
28+
addDeserializer(OffsetDateTime.class, RFC3339InstantDeserializer.OFFSET_DATE_TIME);
29+
addDeserializer(ZonedDateTime.class, RFC3339InstantDeserializer.ZONED_DATE_TIME);
30+
}
31+
}

samples/client/echo_api/java/native/.openapi-generator/FILES

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ src/main/java/org/openapitools/client/Configuration.java
3939
src/main/java/org/openapitools/client/JSON.java
4040
src/main/java/org/openapitools/client/Pair.java
4141
src/main/java/org/openapitools/client/RFC3339DateFormat.java
42+
src/main/java/org/openapitools/client/RFC3339InstantDeserializer.java
43+
src/main/java/org/openapitools/client/RFC3339JavaTimeModule.java
4244
src/main/java/org/openapitools/client/ServerConfiguration.java
4345
src/main/java/org/openapitools/client/ServerVariable.java
4446
src/main/java/org/openapitools/client/api/AuthApi.java

samples/client/echo_api/java/native/src/main/java/org/openapitools/client/ApiClient.java

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ protected ObjectMapper createDefaultObjectMapper() {
203203
mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
204204
mapper.registerModule(new JavaTimeModule());
205205
mapper.registerModule(new JsonNullableModule());
206+
mapper.registerModule(new RFC3339JavaTimeModule());
206207
return mapper;
207208
}
208209

0 commit comments

Comments
 (0)