Skip to content

Commit c0703e3

Browse files
committed
[#19921] Add RFC 3339 compatible Jackson module for java.time types
1 parent 266193f commit c0703e3

File tree

172 files changed

+5788
-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.

172 files changed

+5788
-5
lines changed

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

Lines changed: 2 additions & 0 deletions
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");
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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.deser.InstantDeserializer;
18+
19+
{{>generatedAnnotation}}
20+
public class RFC3339InstantDeserializer<T extends Temporal> extends InstantDeserializer<T> {
21+
22+
public static final RFC3339InstantDeserializer<Instant> INSTANT = new RFC3339InstantDeserializer<>(
23+
Instant.class, DateTimeFormatter.ISO_INSTANT,
24+
Instant::from,
25+
a -> Instant.ofEpochMilli( a.value ),
26+
a -> Instant.ofEpochSecond( a.integer, a.fraction ),
27+
null,
28+
true // yes, replace zero offset with Z
29+
);
30+
31+
public static final RFC3339InstantDeserializer<OffsetDateTime> OFFSET_DATE_TIME = new RFC3339InstantDeserializer<>(
32+
OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME,
33+
OffsetDateTime::from,
34+
a -> OffsetDateTime.ofInstant( Instant.ofEpochMilli( a.value ), a.zoneId ),
35+
a -> OffsetDateTime.ofInstant( Instant.ofEpochSecond( a.integer, a.fraction ), a.zoneId ),
36+
(d, z) -> ( d.isEqual( OffsetDateTime.MIN ) || d.isEqual( OffsetDateTime.MAX ) ?
37+
d :
38+
d.withOffsetSameInstant( z.getRules().getOffset( d.toLocalDateTime() ) ) ),
39+
true // yes, replace zero offset with Z
40+
);
41+
42+
public static final RFC3339InstantDeserializer<ZonedDateTime> ZONED_DATE_TIME = new RFC3339InstantDeserializer<>(
43+
ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME,
44+
ZonedDateTime::from,
45+
a -> ZonedDateTime.ofInstant( Instant.ofEpochMilli( a.value ), a.zoneId ),
46+
a -> ZonedDateTime.ofInstant( Instant.ofEpochSecond( a.integer, a.fraction ), a.zoneId ),
47+
ZonedDateTime::withZoneSameInstant,
48+
false // keep zero offset and Z separate since zones explicitly supported
49+
);
50+
51+
protected RFC3339InstantDeserializer(
52+
Class<T> supportedType,
53+
DateTimeFormatter formatter,
54+
Function<TemporalAccessor, T> parsedToValue,
55+
Function<FromIntegerArguments, T> fromMilliseconds,
56+
Function<FromDecimalArguments, T> fromNanoseconds,
57+
BiFunction<T, ZoneId, T> adjust,
58+
boolean replaceZeroOffsetAsZ) {
59+
super(
60+
supportedType,
61+
formatter,
62+
parsedToValue,
63+
fromMilliseconds,
64+
fromNanoseconds,
65+
adjust,
66+
replaceZeroOffsetAsZ
67+
);
68+
}
69+
70+
@Override
71+
protected T _fromString(JsonParser p, DeserializationContext ctxt, String string0) throws IOException {
72+
return super._fromString(p, ctxt, string0.replace( ' ', 'T' ));
73+
}
74+
}
Lines changed: 20 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ public class ApiClient {
187187
{{/joda}}
188188
objectMapper.registerModule(new JavaTimeModule());
189189
{{#openApiNullable}}
190+
objectMapper.registerModule(new RFC3339JavaTimeModule());
190191
JsonNullableModule jnm = new JsonNullableModule();
191192
objectMapper.registerModule(jnm);
192193
{{/openApiNullable}}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class JSON implements ContextResolver<ObjectMapper> {
4545
{{#openApiNullable}}
4646
.addModule(new JsonNullableModule())
4747
{{/openApiNullable}}
48+
.addModule(new RFC3339JavaTimeModule())
4849
.build();
4950
}
5051

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class JSON implements ContextResolver<ObjectMapper> {
4545
{{#openApiNullable}}
4646
.addModule(new JsonNullableModule())
4747
{{/openApiNullable}}
48+
.addModule(new RFC3339JavaTimeModule())
4849
.build();
4950
}
5051

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ public void testSupportedSecuritySchemesJersey() {
519519

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

522-
assertThat(files).hasSize(32);
522+
assertThat(files).hasSize(34);
523523
validateJavaSourceFiles(files);
524524
assertThat(output.resolve("src/main/java/xyz/abcdef/api/DefaultApi.java")).content().contains(
525525
"public class DefaultApi",
@@ -586,7 +586,7 @@ public void testSupportedSecuritySchemesJersey() {
586586

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

589-
assertThat(files).hasSize(35);
589+
assertThat(files).hasSize(37);
590590

591591
validateJavaSourceFiles(files);
592592
assertThat(output.resolve("src/main/java/xyz/abcdef/api/PingApi.java")).content().contains(
@@ -1389,7 +1389,7 @@ public void testRestTemplateWithFreeFormInQueryParameters() {
13891389
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
13901390

13911391
validateJavaSourceFiles(files);
1392-
assertThat(files).hasSize(35);
1392+
assertThat(files).hasSize(37);
13931393
TestUtils.assertFileContains(output.resolve("src/main/java/xyz/abcdef/ApiClient.java"),
13941394
"public static String urlEncode(String s) { return URLEncoder.encode(s,"
13951395
+ " UTF_8).replaceAll(\"\\\\+\", \"%20\"); }"
@@ -1411,7 +1411,7 @@ public void testRestTemplateWithFreeFormInQueryParameters() {
14111411
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
14121412

14131413
validateJavaSourceFiles(files);
1414-
assertThat(files).hasSize(38);
1414+
assertThat(files).hasSize(40);
14151415
assertThat(output.resolve("src/main/java/xyz/abcdef/api/DefaultApi.java")).content()
14161416
.contains(
14171417
"localVarQueryParams.addAll(ApiClient.parameterToPairs(\"since\", queryObject.getSince()));",

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

Lines changed: 1 addition & 1 deletion
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"),

0 commit comments

Comments
 (0)