Skip to content

Commit 475e2fb

Browse files
committed
Add jackson3 support for webclient
1 parent 68dc149 commit 475e2fb

9 files changed

Lines changed: 205 additions & 38 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
useSpringBoot4: true
10+
useJackson3: true
11+
useJakartaEe: true
12+
openApiNullable: false

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ public JavaClientCodegen() {
307307
serializationLibrary.setEnum(serializationOptions);
308308
cliOptions.add(serializationLibrary);
309309
cliOptions.add(CliOption.newBoolean(USE_SPRING_BOOT4, "Generate code and provide dependencies for use with Spring Boot 4.x.", useSpringBoot4));
310-
cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Set it in order to use jackson 3 dependencies (only allowed when `" + USE_SPRING_BOOT4 + "` is set and incompatible with `"+OPENAPI_NULLABLE+"`).", useJackson3)); // Ensure the OAS 3.x discriminator mappings include any descendent schemas that allOf
310+
cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Set it in order to use jackson 3 dependencies (only allowed when `" + USE_SPRING_BOOT4 + "` is set and incompatible with `"+OPENAPI_NULLABLE+"`). For 'java' generator: only supported for 'native' and 'webclient' libraries.", useJackson3)); // Ensure the OAS 3.x discriminator mappings include any descendent schemas that allOf
311311
// inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values,
312312
// and the discriminator mapping schemas in the OAS document.
313313
this.setLegacyDiscriminatorBehavior(false);
@@ -378,16 +378,20 @@ public void processOpts() {
378378
convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, this::setUseOneOfDiscriminatorLookup);
379379
convertPropertyToBooleanAndWriteBack(USE_JACKSON_3, this::setUseJackson3);
380380
convertPropertyToBooleanAndWriteBack(USE_SPRING_BOOT4, this::setUseSpringBoot4);
381-
if(isUseJackson3() && !isUseSpringBoot4()){
382-
throw new IllegalArgumentException("useJackson3 is only available with Spring Boot >= 4");
383-
}
384-
if(isUseJackson3() && isOpenApiNullable()){
385-
throw new IllegalArgumentException("openApiNullable cannot be set with useJackson3");
381+
382+
if (isUseJackson3()) {
383+
if (!isUseSpringBoot4()) {
384+
throw new IllegalArgumentException("useJackson3 is only available with Spring Boot >= 4");
385+
}
386+
if (isOpenApiNullable()) {
387+
throw new IllegalArgumentException("openApiNullable cannot be set with useJackson3");
388+
}
386389
}
387390

388-
if(this.useJackson3){
391+
if (isUseJackson3()) {
389392
this.applyJackson3Package();
390-
} else {
393+
}
394+
else {
391395
this.applyJackson2Package();
392396
}
393397

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: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@
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.json.JsonMapper;
14+
{{/useJackson3}}
915
{{#openApiNullable}}
16+
{{^useJackson3}}
1017
import org.openapitools.jackson.nullable.JsonNullableModule;
18+
{{/useJackson3}}
19+
{{#useJackson3}}
20+
import org.openapitools.jackson.nullable.JsonNullableJackson3Module;
21+
{{/useJackson3}}
1122
{{/openApiNullable}}
1223
{{#generateClientAsBean}}
1324
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,8 +38,14 @@ import org.springframework.http.client.ClientHttpRequestExecution;
2738
import org.springframework.http.client.ClientHttpRequestInterceptor;
2839
import org.springframework.http.client.ClientHttpResponse;
2940
import org.springframework.http.client.reactive.ClientHttpRequest;
41+
{{^useJackson3}}
3042
import org.springframework.http.codec.json.Jackson2JsonDecoder;
3143
import org.springframework.http.codec.json.Jackson2JsonEncoder;
44+
{{/useJackson3}}
45+
{{#useJackson3}}
46+
import org.springframework.http.codec.json.JacksonJsonDecoder;
47+
import org.springframework.http.codec.json.JacksonJsonEncoder;
48+
{{/useJackson3}}
3249
{{#generateClientAsBean}}
3350
import org.springframework.stereotype.Component;
3451
{{/generateClientAsBean}}
@@ -110,15 +127,15 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
110127

111128
protected final WebClient webClient;
112129
protected final DateFormat dateFormat;
113-
protected final ObjectMapper objectMapper;
130+
protected final {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper;
114131

115132
protected Map<String, Authentication> authentications;
116133

117134

118135
public ApiClient() {
119136
this.dateFormat = createDefaultDateFormat();
120-
this.objectMapper = createDefaultObjectMapper(this.dateFormat);
121-
this.webClient = buildWebClient(this.objectMapper);
137+
this.mapper = createDefaultMapper(this.dateFormat);
138+
this.webClient = buildWebClient(this.mapper);
122139
this.init();
123140
}
124141

@@ -129,18 +146,18 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
129146
this(Optional.ofNullable(webClient).orElseGet(() -> buildWebClient()), createDefaultDateFormat());
130147
}
131148

132-
public ApiClient(ObjectMapper mapper, DateFormat format) {
133-
this(buildWebClient(mapper.copy()), format);
149+
public ApiClient({{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper, DateFormat format) {
150+
this(buildWebClient(mapper{{^useJackson3}}.copy(){{/useJackson3}}), format);
134151
}
135152

136-
public ApiClient(WebClient webClient, ObjectMapper mapper, DateFormat format) {
137-
this(Optional.ofNullable(webClient).orElseGet(() -> buildWebClient(mapper.copy())), format);
153+
public ApiClient(WebClient webClient, {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper, DateFormat format) {
154+
this(Optional.ofNullable(webClient).orElseGet(() -> buildWebClient(mapper{{^useJackson3}}.copy(){{/useJackson3}})), format);
138155
}
139156

140157
protected ApiClient(WebClient webClient, DateFormat format) {
141158
this.webClient = webClient;
142159
this.dateFormat = format;
143-
this.objectMapper = createDefaultObjectMapper(format);
160+
this.mapper = createDefaultMapper(format);
144161
this.init();
145162
}
146163

@@ -150,10 +167,11 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
150167
return dateFormat;
151168
}
152169

153-
public static ObjectMapper createDefaultObjectMapper(@Nullable DateFormat dateFormat) {
170+
public static {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} createDefaultMapper(@Nullable DateFormat dateFormat) {
154171
if (null == dateFormat) {
155172
dateFormat = createDefaultDateFormat();
156173
}
174+
{{^useJackson3}}
157175
ObjectMapper mapper = new ObjectMapper();
158176
mapper.setDateFormat(dateFormat);
159177
mapper.registerModule(new JavaTimeModule());
@@ -163,6 +181,14 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
163181
mapper.registerModule(jnm);
164182
{{/openApiNullable}}
165183
return mapper;
184+
{{/useJackson3}}
185+
{{#useJackson3}}
186+
return JsonMapper
187+
.builder()
188+
.defaultDateFormat(dateFormat)
189+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}})
190+
.build();
191+
{{/useJackson3}}
166192
}
167193

168194
protected void init() {
@@ -178,15 +204,21 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
178204

179205
/**
180206
* Build the WebClientBuilder used to make WebClient.
181-
* @param mapper ObjectMapper used for serialize/deserialize
207+
* @param mapper {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} used for serialize/deserialize
182208
* @return WebClient
183209
*/
184-
public static WebClient.Builder buildWebClientBuilder(ObjectMapper mapper) {
210+
public static WebClient.Builder buildWebClientBuilder({{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper) {
185211
ExchangeStrategies strategies = ExchangeStrategies
186212
.builder()
187213
.codecs(clientDefaultCodecsConfigurer -> {
214+
{{^useJackson3}}
188215
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON));
189216
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON));
217+
{{/useJackson3}}
218+
{{#useJackson3}}
219+
clientDefaultCodecsConfigurer.defaultCodecs().jacksonJsonEncoder(new JacksonJsonEncoder(mapper));
220+
clientDefaultCodecsConfigurer.defaultCodecs().jacksonJsonDecoder(new JacksonJsonDecoder(mapper));
221+
{{/useJackson3}}
190222
}).build();
191223
WebClient.Builder webClientBuilder = WebClient.builder().exchangeStrategies(strategies);
192224
return webClientBuilder;
@@ -197,15 +229,15 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
197229
* @return WebClient
198230
*/
199231
public static WebClient.Builder buildWebClientBuilder() {
200-
return buildWebClientBuilder(createDefaultObjectMapper(null));
232+
return buildWebClientBuilder(createDefaultMapper(null));
201233
}
202234

203235
/**
204236
* Build the WebClient used to make HTTP requests.
205-
* @param mapper ObjectMapper used for serialize/deserialize
237+
* @param mapper {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} used for serialize/deserialize
206238
* @return WebClient
207239
*/
208-
public static WebClient buildWebClient(ObjectMapper mapper) {
240+
public static WebClient buildWebClient({{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper) {
209241
return buildWebClientBuilder(mapper).build();
210242
}
211243

@@ -214,7 +246,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
214246
* @return WebClient
215247
*/
216248
public static WebClient buildWebClient() {
217-
return buildWebClientBuilder(createDefaultObjectMapper(null)).build();
249+
return buildWebClientBuilder(createDefaultMapper(null)).build();
218250
}
219251

220252
/**
@@ -403,11 +435,11 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
403435
}
404436

405437
/**
406-
* Get the ObjectMapper used to make HTTP requests.
407-
* @return ObjectMapper objectMapper
438+
* Get the {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} used to make HTTP requests.
439+
* @return {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper
408440
*/
409-
public ObjectMapper getObjectMapper() {
410-
return objectMapper;
441+
public {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} getMapper() {
442+
return mapper;
411443
}
412444

413445
/**
@@ -456,20 +488,30 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
456488
if (value instanceof Collection) {
457489
valueCollection = (Collection<?>) value;
458490
} else {
491+
{{^useJackson3}}
459492
try {
460-
return parameterToMultiValueMap(collectionFormat, name, objectMapper.writeValueAsString(value));
493+
return parameterToMultiValueMap(collectionFormat, name, mapper.writeValueAsString(value));
461494
} catch (JsonProcessingException e) {
462495
throw new RuntimeException(e);
463496
}
497+
{{/useJackson3}}
498+
{{#useJackson3}}
499+
return parameterToMultiValueMap(collectionFormat, name, mapper.writeValueAsString(value));
500+
{{/useJackson3}}
464501
}
465502

466503
List<String> values = new ArrayList<>();
467504
for(Object o : valueCollection) {
505+
{{^useJackson3}}
468506
try {
469-
values.add(objectMapper.writeValueAsString(o));
507+
values.add(mapper.writeValueAsString(o));
470508
} catch (JsonProcessingException e) {
471509
throw new RuntimeException(e);
472510
}
511+
{{/useJackson3}}
512+
{{#useJackson3}}
513+
values.add(mapper.writeValueAsString(o));
514+
{{/useJackson3}}
473515
}
474516
return parameterToMultiValueMap(collectionFormat, name, "[" + StringUtils.collectionToDelimitedString(values, collectionFormat.separator) + "]");
475517
}

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,12 @@ 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"
@@ -144,8 +149,13 @@ ext {
144149
reactor_version = "3.4.34"
145150
reactor_netty_version = "1.2.8"
146151
{{/useJakartaEe}}
152+
{{^useJackson3}}
147153
jackson_version = "2.19.2"
148-
jackson_databind_version = "2.19.2"
154+
{{/useJackson3}}
155+
{{#useJackson3}}
156+
jackson_version = "3.1.0"
157+
jackson_annotations_version = "2.19.2"
158+
{{/useJackson3}}
149159
{{#openApiNullable}}
150160
jackson_databind_nullable_version = "0.2.9"
151161
{{/openApiNullable}}
@@ -166,17 +176,25 @@ dependencies {
166176
implementation "io.projectreactor:reactor-core:$reactor_version"
167177
implementation "org.springframework.boot:spring-boot-starter-webflux:$spring_boot_version"
168178
implementation "io.projectreactor.netty:reactor-netty-http:$reactor_netty_version"
179+
{{^useJackson3}}
169180
implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
170-
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
171-
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_databind_version"
181+
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
172182
implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
183+
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
184+
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
185+
{{/useJackson3}}
186+
{{#useJackson3}}
187+
implementation "tools.jackson.core:jackson-core:$jackson_version"
188+
implementation "tools.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
189+
implementation "tools.jackson.core:jackson-databind:$jackson_version"
190+
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_annotations_version"
191+
{{/useJackson3}}
173192
{{#openApiNullable}}
174193
implementation "org.openapitools:jackson-databind-nullable:$jackson_databind_nullable_version"
175194
{{/openApiNullable}}
176195
{{#joda}}
177196
implementation "joda-time:joda-time:$jodatime_version"
178197
{{/joda}}
179-
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
180198
implementation "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version"
181199
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version"
182200
}

0 commit comments

Comments
 (0)