Skip to content

Commit ce481ac

Browse files
committed
Add jackson3 support for webclient
1 parent 8515f5e commit ce481ac

216 files changed

Lines changed: 26200 additions & 7 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
generatorName: java
2+
outputDir: samples/client/petstore/java/webclient-jackson3
3+
library: webclient
4+
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml
5+
templateDir: modules/openapi-generator/src/main/resources/Java
6+
additionalProperties:
7+
artifactId: petstore-webclient-jackson3
8+
hideGenerationTimestamp: "true"
9+
useJackson3: true
10+
useJakartaEe: true

docs/generators/java-microprofile.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
9898
|useEnumCaseInsensitive|Use `equalsIgnoreCase` when String for enum comparison| |false|
9999
|useGzipFeature|Send gzip-encoded requests| |false|
100100
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
101+
|useJackson3|Use Jackson 3 instead of Jackson 2 for JSON processing. Only supported for 'native' and 'webclient' libraries.| |false|
101102
|useOneOfDiscriminatorLookup|Use the discriminator's mapping in oneOf to speed up the model lookup. IMPORTANT: Validation (e.g. one and only one match in oneOf's schemas) will be skipped. Only jersey2, jersey3, native, okhttp-gson support this option.| |false|
102103
|useOneOfInterfaces|whether to use a java interface to describe a set of oneOf options, where each option is a class that implements the interface| |false|
103104
|usePlayWS|Use Play! Async HTTP client (Play WS API)| |false|

docs/generators/java.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
9797
|useBeanValidation|Use BeanValidation API annotations| |false|
9898
|useEnumCaseInsensitive|Use `equalsIgnoreCase` when String for enum comparison| |false|
9999
|useGzipFeature|Send gzip-encoded requests| |false|
100+
|useJackson3|Use Jackson 3 instead of Jackson 2 for JSON processing. Only supported for 'native' and 'webclient' libraries.| |false|
100101
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
101102
|useOneOfDiscriminatorLookup|Use the discriminator's mapping in oneOf to speed up the model lookup. IMPORTANT: Validation (e.g. one and only one match in oneOf's schemas) will be skipped. Only jersey2, jersey3, native, okhttp-gson support this option.| |false|
102103
|useOneOfInterfaces|whether to use a java interface to describe a set of oneOf options, where each option is a class that implements the interface| |false|

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
107107
public static final String SUPPORT_VERTX_FUTURE = "supportVertxFuture";
108108
public static final String USE_SEALED_ONE_OF_INTERFACES = "useSealedOneOfInterfaces";
109109
public static final String USE_UNARY_INTERCEPTOR = "useUnaryInterceptor";
110+
public static final String USE_JACKSON_3 = "useJackson3";
110111

111112
// Internal configurations
112113
public static final String SINGLE_REQUEST_PARAMETER = "singleRequestParameter";
@@ -152,6 +153,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
152153
@Setter protected boolean supportVertxFuture = false;
153154
@Setter protected boolean useSealedOneOfInterfaces = false;
154155
@Setter protected boolean useUnaryInterceptor = false;
156+
@Getter @Setter protected boolean useJackson3 = false;
155157

156158
protected String authFolder;
157159
/**
@@ -262,6 +264,7 @@ public JavaClientCodegen() {
262264
cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries."));
263265
cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive));
264266
cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", this.failOnUnknownProperties));
267+
cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Use Jackson 3 instead of Jackson 2 for JSON processing. Only supported for 'native' and 'webclient' libraries.", this.useJackson3));
265268
cliOptions.add(CliOption.newBoolean(SUPPORT_VERTX_FUTURE, "Also generate api methods that return a vertx Future instead of taking a callback. Only `vertx` supports this option. Requires vertx 4 or greater.", this.supportVertxFuture));
266269
cliOptions.add(CliOption.newBoolean(USE_SEALED_ONE_OF_INTERFACES, "Generate the oneOf interfaces as sealed interfaces. Only supported for WebClient and RestClient.", this.useSealedOneOfInterfaces));
267270
cliOptions.add(CliOption.newBoolean(USE_UNARY_INTERCEPTOR, "If true it will generate ResponseInterceptors using a UnaryOperator. This can be usefull for manipulating the request before it gets passed, for example doing your own decryption", this.useUnaryInterceptor));
@@ -454,6 +457,7 @@ public void processOpts() {
454457
convertPropertyToBooleanAndWriteBack(WEBCLIENT_BLOCKING_OPERATIONS, op -> webclientBlockingOperations = op);
455458
convertPropertyToBooleanAndWriteBack(FAIL_ON_UNKNOWN_PROPERTIES, this::setFailOnUnknownProperties);
456459
convertPropertyToBooleanAndWriteBack(SUPPORT_VERTX_FUTURE, this::setSupportVertxFuture);
460+
convertPropertyToBooleanAndWriteBack(USE_JACKSON_3, this::setUseJackson3);
457461

458462
// add URL query deepObject support to native, apache-httpclient by default
459463
if (!additionalProperties.containsKey(SUPPORT_URL_QUERY)) {
@@ -810,6 +814,20 @@ public void processOpts() {
810814
break;
811815
}
812816

817+
// Jackson 3 support: update importMapping to use tools.jackson.* packages
818+
if (useJackson3) {
819+
if (!libNative && !libWebClient) {
820+
LOGGER.warn("useJackson3 option is currently only supported for library=native|webclient. Ignoring useJackson3.");
821+
useJackson3 = false;
822+
additionalProperties.put(USE_JACKSON_3, false);
823+
} else {
824+
if (openApiNullable) {
825+
LOGGER.warn("openApiNullable with useJackson3=true: JsonNullableModule is not yet available for Jackson 3. " +
826+
"JsonNullable types will be generated but module registration will be skipped.");
827+
}
828+
}
829+
}
830+
813831
if (isLibrary(FEIGN)) {
814832
additionalProperties.put("feign-okhttp", "true");
815833
} else if (isLibrary(FEIGN_HC5)) {
@@ -1298,4 +1316,4 @@ public List<VendorExtension> getSupportedVendorExtensions() {
12981316
extensions.add(VendorExtension.X_WEBCLIENT_BLOCKING);
12991317
return extensions;
13001318
}
1301-
}
1319+
}

modules/openapi-generator/src/main/resources/Java/RFC3339DateFormat.mustache

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
{{>licenseInfo}}
22
package {{invokerPackage}};
33

4+
{{^useJackson3}}
45
import com.fasterxml.jackson.databind.util.StdDateFormat;
6+
{{/useJackson3}}
7+
{{#useJackson3}}
8+
import tools.jackson.databind.util.StdDateFormat;
9+
{{/useJackson3}}
510

611
import java.text.DateFormat;
712
import java.text.FieldPosition;

modules/openapi-generator/src/main/resources/Java/RFC3339InstantDeserializer.mustache

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{{>licenseInfo}}
22
package {{invokerPackage}};
33

4+
{{^useJackson3}}
45
import java.io.IOException;
6+
{{/useJackson3}}
57
import java.time.Instant;
68
import java.time.OffsetDateTime;
79
import java.time.ZoneId;
@@ -12,10 +14,19 @@ import java.time.temporal.TemporalAccessor;
1214
import java.util.function.BiFunction;
1315
import java.util.function.Function;
1416

17+
{{^useJackson3}}
1518
import com.fasterxml.jackson.core.JsonParser;
1619
import com.fasterxml.jackson.databind.DeserializationContext;
1720
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
1821
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
22+
{{/useJackson3}}
23+
{{#useJackson3}}
24+
import tools.jackson.core.JacksonException;
25+
import tools.jackson.core.JsonParser;
26+
import tools.jackson.databind.DeserializationContext;
27+
import tools.jackson.datatype.jsr310.JavaTimeFeature;
28+
import tools.jackson.datatype.jsr310.deser.InstantDeserializer;
29+
{{/useJackson3}}
1930

2031
{{>generatedAnnotation}}
2132

@@ -84,7 +95,7 @@ public class RFC3339InstantDeserializer<T extends Temporal> extends InstantDeser
8495
}
8596

8697
@Override
87-
protected T _fromString(JsonParser p, DeserializationContext ctxt, String string0) throws IOException {
98+
protected T _fromString(JsonParser p, DeserializationContext ctxt, String string0) throws {{^useJackson3}}IOException{{/useJackson3}}{{#useJackson3}}JacksonException{{/useJackson3}} {
8899
return super._fromString(p, ctxt, string0.replace( ' ', 'T' ));
89100
}
90101
}

modules/openapi-generator/src/main/resources/Java/RFC3339JavaTimeModule.mustache

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ import java.time.Instant;
55
import java.time.OffsetDateTime;
66
import java.time.ZonedDateTime;
77

8+
{{^useJackson3}}
89
import com.fasterxml.jackson.databind.module.SimpleModule;
910
import com.fasterxml.jackson.databind.Module.SetupContext;
11+
{{/useJackson3}}
12+
{{#useJackson3}}
13+
import tools.jackson.databind.module.SimpleModule;
14+
{{/useJackson3}}
1015

1116
{{>generatedAnnotation}}
1217

@@ -15,8 +20,14 @@ public class RFC3339JavaTimeModule extends SimpleModule {
1520
1621
public RFC3339JavaTimeModule() {
1722
super("RFC3339JavaTimeModule");
23+
{{#useJackson3}}
24+
addDeserializer(Instant.class, RFC3339InstantDeserializer.INSTANT);
25+
addDeserializer(OffsetDateTime.class, RFC3339InstantDeserializer.OFFSET_DATE_TIME);
26+
addDeserializer(ZonedDateTime.class, RFC3339InstantDeserializer.ZONED_DATE_TIME);
27+
{{/useJackson3}}
1828
}
1929

30+
{{^useJackson3}}
2031
@Override
2132
public void setupModule(SetupContext context) {
2233
super.setupModule(context);
@@ -25,5 +36,6 @@ public class RFC3339JavaTimeModule extends SimpleModule {
2536
addDeserializer(OffsetDateTime.class, RFC3339InstantDeserializer.OFFSET_DATE_TIME);
2637
addDeserializer(ZonedDateTime.class, RFC3339InstantDeserializer.ZONED_DATE_TIME);
2738
}
39+
{{/useJackson3}}
2840

29-
}
41+
}

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

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,24 @@
22

33
package {{invokerPackage}};
44

5+
{{^useJackson3}}
56
import com.fasterxml.jackson.core.JsonProcessingException;
67
import com.fasterxml.jackson.databind.DeserializationFeature;
78
import com.fasterxml.jackson.databind.ObjectMapper;
89
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
10+
{{/useJackson3}}
11+
{{#useJackson3}}
12+
import tools.jackson.databind.DeserializationFeature;
13+
import tools.jackson.databind.JsonMapper;
14+
import tools.jackson.databind.ObjectMapper;
15+
{{/useJackson3}}
916
{{#openApiNullable}}
17+
{{^useJackson3}}
1018
import org.openapitools.jackson.nullable.JsonNullableModule;
19+
{{/useJackson3}}
20+
{{#useJackson3}}
21+
import org.openapitools.jackson.nullable.JsonNullableJackson3Module;
22+
{{/useJackson3}}
1123
{{/openApiNullable}}
1224
{{#generateClientAsBean}}
1325
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,8 +39,14 @@ import org.springframework.http.client.ClientHttpRequestExecution;
2739
import org.springframework.http.client.ClientHttpRequestInterceptor;
2840
import org.springframework.http.client.ClientHttpResponse;
2941
import org.springframework.http.client.reactive.ClientHttpRequest;
42+
{{^useJackson3}}
3043
import org.springframework.http.codec.json.Jackson2JsonDecoder;
3144
import org.springframework.http.codec.json.Jackson2JsonEncoder;
45+
{{/useJackson3}}
46+
{{#useJackson3}}
47+
import org.springframework.http.codec.json.JacksonJsonDecoder;
48+
import org.springframework.http.codec.json.JacksonJsonEncoder;
49+
{{/useJackson3}}
3250
{{#generateClientAsBean}}
3351
import org.springframework.stereotype.Component;
3452
{{/generateClientAsBean}}
@@ -130,11 +148,11 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
130148
}
131149

132150
public ApiClient(ObjectMapper mapper, DateFormat format) {
133-
this(buildWebClient(mapper.copy()), format);
151+
this(buildWebClient(mapper{{^useJackson3}}.copy(){{/useJackson3}}), format);
134152
}
135153

136154
public ApiClient(WebClient webClient, ObjectMapper mapper, DateFormat format) {
137-
this(Optional.ofNullable(webClient).orElseGet(() -> buildWebClient(mapper.copy())), format);
155+
this(Optional.ofNullable(webClient).orElseGet(() -> buildWebClient(mapper{{^useJackson3}}.copy(){{/useJackson3}})), format);
138156
}
139157

140158
protected ApiClient(WebClient webClient, DateFormat format) {
@@ -154,6 +172,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
154172
if (null == dateFormat) {
155173
dateFormat = createDefaultDateFormat();
156174
}
175+
{{^useJackson3}}
157176
ObjectMapper mapper = new ObjectMapper();
158177
mapper.setDateFormat(dateFormat);
159178
mapper.registerModule(new JavaTimeModule());
@@ -163,6 +182,17 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
163182
mapper.registerModule(jnm);
164183
{{/openApiNullable}}
165184
return mapper;
185+
{{/useJackson3}}
186+
{{#useJackson3}}
187+
return JsonMapper
188+
.builder()
189+
.setDateFormat(dateFormat)
190+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}})
191+
{{#openApiNullable}}
192+
.addModule(new JsonNullableJackson3Module())
193+
{{/openApiNullable}}
194+
.build();
195+
{{/useJackson3}}
166196
}
167197

168198
protected void init() {
@@ -185,8 +215,14 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
185215
ExchangeStrategies strategies = ExchangeStrategies
186216
.builder()
187217
.codecs(clientDefaultCodecsConfigurer -> {
218+
{{^useJackson3}}
188219
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON));
189220
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON));
221+
{{/useJackson3}}
222+
{{#useJackson3}}
223+
clientDefaultCodecsConfigurer.defaultCodecs().jacksonJsonEncoder(new JacksonJsonEncoder(mapper));
224+
clientDefaultCodecsConfigurer.defaultCodecs().jacksonJsonDecoder(new JacksonJsonDecoder(mapper));
225+
{{/useJackson3}}
190226
}).build();
191227
WebClient.Builder webClientBuilder = WebClient.builder().exchangeStrategies(strategies);
192228
return webClientBuilder;
@@ -456,20 +492,30 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
456492
if (value instanceof Collection) {
457493
valueCollection = (Collection<?>) value;
458494
} else {
495+
{{^useJackson3}}
459496
try {
460497
return parameterToMultiValueMap(collectionFormat, name, objectMapper.writeValueAsString(value));
461498
} catch (JsonProcessingException e) {
462499
throw new RuntimeException(e);
463500
}
501+
{{/useJackson3}}
502+
{{#useJackson3}}
503+
return parameterToMultiValueMap(collectionFormat, name, objectMapper.writeValueAsString(value));
504+
{{/useJackson3}}
464505
}
465506

466507
List<String> values = new ArrayList<>();
467508
for(Object o : valueCollection) {
509+
{{^useJackson3}}
468510
try {
469511
values.add(objectMapper.writeValueAsString(o));
470512
} catch (JsonProcessingException e) {
471513
throw new RuntimeException(e);
472514
}
515+
{{/useJackson3}}
516+
{{#useJackson3}}
517+
values.add(objectMapper.writeValueAsString(o));
518+
{{/useJackson3}}
473519
}
474520
return parameterToMultiValueMap(collectionFormat, name, "[" + StringUtils.collectionToDelimitedString(values, collectionFormat.separator) + "]");
475521
}

modules/openapi-generator/src/main/resources/Java/libraries/webclient/build.gradle.mustache

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,37 @@ ext {
131131
swagger_annotations_version = "2.2.9"
132132
{{/swagger2AnnotationLibrary}}
133133
{{#useJakartaEe}}
134+
{{^useJackson3}}
134135
spring_boot_version = "3.2.12"
136+
{{/useJackson3}}
137+
{{#useJackson3}}
138+
spring_boot_version = "4.0.3"
139+
{{/useJackson3}}
135140
jakarta_annotation_version = "2.1.1"
136141
beanvalidation_version = "3.0.2"
137142
reactor_version = "3.5.12"
138143
reactor_netty_version = "1.2.8"
139144
{{/useJakartaEe}}
140145
{{^useJakartaEe}}
146+
{{^useJackson3}}
141147
spring_boot_version = "2.7.17"
148+
{{/useJackson3}}
149+
{{#useJackson3}}
150+
spring_boot_version = "4.0.3"
151+
{{/useJackson3}}
142152
jakarta_annotation_version = "1.3.5"
143153
beanvalidation_version = "2.0.2"
144154
reactor_version = "3.4.34"
145155
reactor_netty_version = "1.2.8"
146156
{{/useJakartaEe}}
157+
{{^useJackson3}}
147158
jackson_version = "2.19.2"
159+
{{/useJackson3}}
160+
{{#useJackson3}}
161+
jackson_version = "3.1.0"
162+
jackson_databind_version = "3.1.0"
163+
jackson_annotations_version = "2.19.2"
164+
{{/useJackson3}}
148165
jackson_databind_version = "2.19.2"
149166
{{#openApiNullable}}
150167
jackson_databind_nullable_version = "0.2.9"
@@ -166,17 +183,25 @@ dependencies {
166183
implementation "io.projectreactor:reactor-core:$reactor_version"
167184
implementation "org.springframework.boot:spring-boot-starter-webflux:$spring_boot_version"
168185
implementation "io.projectreactor.netty:reactor-netty-http:$reactor_netty_version"
186+
{{^useJackson3}}
169187
implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
170-
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
171188
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_databind_version"
172189
implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
190+
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
191+
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
192+
{{/useJackson3}}
193+
{{#useJackson3}}
194+
implementation "tools.jackson.core:jackson-core:$jackson_version"
195+
implementation "tools.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
196+
implementation "tools.jackson.core:jackson-databind:$jackson_databind_version"
197+
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_annotations_version"
198+
{{/useJackson3}}
173199
{{#openApiNullable}}
174200
implementation "org.openapitools:jackson-databind-nullable:$jackson_databind_nullable_version"
175201
{{/openApiNullable}}
176202
{{#joda}}
177203
implementation "joda-time:joda-time:$jodatime_version"
178204
{{/joda}}
179-
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
180205
implementation "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version"
181206
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version"
182207
}

0 commit comments

Comments
 (0)