Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions bin/configs/java-webclient-jackson3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
generatorName: java
outputDir: samples/client/petstore/java/webclient-jackson3
library: webclient
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/Java
additionalProperties:
artifactId: petstore-webclient-jackson3
hideGenerationTimestamp: "true"
useJackson3: true
useJakartaEe: true
1 change: 1 addition & 0 deletions docs/generators/java-microprofile.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useBeanValidation|Use BeanValidation API annotations| |false|
|useEnumCaseInsensitive|Use `equalsIgnoreCase` when String for enum comparison| |false|
|useGzipFeature|Send gzip-encoded requests| |false|
|useJackson3|Use Jackson 3 instead of Jackson 2 for JSON processing. Only supported for 'native' and 'webclient' libraries.| |false|
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
|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|
|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|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useBeanValidation|Use BeanValidation API annotations| |false|
|useEnumCaseInsensitive|Use `equalsIgnoreCase` when String for enum comparison| |false|
|useGzipFeature|Send gzip-encoded requests| |false|
|useJackson3|Use Jackson 3 instead of Jackson 2 for JSON processing. Only supported for 'native' and 'webclient' libraries.| |false|
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
|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|
|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|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
public static final String SUPPORT_VERTX_FUTURE = "supportVertxFuture";
public static final String USE_SEALED_ONE_OF_INTERFACES = "useSealedOneOfInterfaces";
public static final String USE_UNARY_INTERCEPTOR = "useUnaryInterceptor";
public static final String USE_JACKSON_3 = "useJackson3";

// Internal configurations
public static final String SINGLE_REQUEST_PARAMETER = "singleRequestParameter";
Expand Down Expand Up @@ -152,6 +153,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
@Setter protected boolean supportVertxFuture = false;
@Setter protected boolean useSealedOneOfInterfaces = false;
@Setter protected boolean useUnaryInterceptor = false;
@Getter @Setter protected boolean useJackson3 = false;

protected String authFolder;
/**
Expand Down Expand Up @@ -262,6 +264,7 @@ public JavaClientCodegen() {
cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries."));
cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive));
cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", this.failOnUnknownProperties));
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));
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));
cliOptions.add(CliOption.newBoolean(USE_SEALED_ONE_OF_INTERFACES, "Generate the oneOf interfaces as sealed interfaces. Only supported for WebClient and RestClient.", this.useSealedOneOfInterfaces));
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));
Expand Down Expand Up @@ -454,6 +457,7 @@ public void processOpts() {
convertPropertyToBooleanAndWriteBack(WEBCLIENT_BLOCKING_OPERATIONS, op -> webclientBlockingOperations = op);
convertPropertyToBooleanAndWriteBack(FAIL_ON_UNKNOWN_PROPERTIES, this::setFailOnUnknownProperties);
convertPropertyToBooleanAndWriteBack(SUPPORT_VERTX_FUTURE, this::setSupportVertxFuture);
convertPropertyToBooleanAndWriteBack(USE_JACKSON_3, this::setUseJackson3);

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

// Jackson 3 support: update importMapping to use tools.jackson.* packages
if (useJackson3) {
if (!libNative && !libWebClient) {
LOGGER.warn("useJackson3 option is currently only supported for library=native|webclient. Ignoring useJackson3.");
useJackson3 = false;
additionalProperties.put(USE_JACKSON_3, false);
} else {
if (openApiNullable) {
LOGGER.warn("openApiNullable with useJackson3=true: JsonNullableModule is not yet available for Jackson 3. " +
"JsonNullable types will be generated but module registration will be skipped.");
}
}
}

if (isLibrary(FEIGN)) {
additionalProperties.put("feign-okhttp", "true");
} else if (isLibrary(FEIGN_HC5)) {
Expand Down Expand Up @@ -1298,4 +1316,4 @@ public List<VendorExtension> getSupportedVendorExtensions() {
extensions.add(VendorExtension.X_WEBCLIENT_BLOCKING);
return extensions;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{{>licenseInfo}}
package {{invokerPackage}};

{{^useJackson3}}
import com.fasterxml.jackson.databind.util.StdDateFormat;
{{/useJackson3}}
{{#useJackson3}}
import tools.jackson.databind.util.StdDateFormat;
{{/useJackson3}}

import java.text.DateFormat;
import java.text.FieldPosition;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{{>licenseInfo}}
package {{invokerPackage}};

{{^useJackson3}}
import java.io.IOException;
{{/useJackson3}}
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
Expand All @@ -12,10 +14,19 @@ import java.time.temporal.TemporalAccessor;
import java.util.function.BiFunction;
import java.util.function.Function;

{{^useJackson3}}
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
{{/useJackson3}}
{{#useJackson3}}
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.datatype.jsr310.JavaTimeFeature;
import tools.jackson.datatype.jsr310.deser.InstantDeserializer;
{{/useJackson3}}

{{>generatedAnnotation}}

Expand Down Expand Up @@ -84,7 +95,7 @@ public class RFC3339InstantDeserializer<T extends Temporal> extends InstantDeser
}

@Override
protected T _fromString(JsonParser p, DeserializationContext ctxt, String string0) throws IOException {
protected T _fromString(JsonParser p, DeserializationContext ctxt, String string0) throws {{^useJackson3}}IOException{{/useJackson3}}{{#useJackson3}}JacksonException{{/useJackson3}} {
return super._fromString(p, ctxt, string0.replace( ' ', 'T' ));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;

{{^useJackson3}}
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.Module.SetupContext;
{{/useJackson3}}
{{#useJackson3}}
import tools.jackson.databind.module.SimpleModule;
{{/useJackson3}}

{{>generatedAnnotation}}

Expand All @@ -15,8 +20,14 @@ public class RFC3339JavaTimeModule extends SimpleModule {

public RFC3339JavaTimeModule() {
super("RFC3339JavaTimeModule");
{{#useJackson3}}
addDeserializer(Instant.class, RFC3339InstantDeserializer.INSTANT);
addDeserializer(OffsetDateTime.class, RFC3339InstantDeserializer.OFFSET_DATE_TIME);
addDeserializer(ZonedDateTime.class, RFC3339InstantDeserializer.ZONED_DATE_TIME);
{{/useJackson3}}
}

{{^useJackson3}}
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
Expand All @@ -25,5 +36,6 @@ public class RFC3339JavaTimeModule extends SimpleModule {
addDeserializer(OffsetDateTime.class, RFC3339InstantDeserializer.OFFSET_DATE_TIME);
addDeserializer(ZonedDateTime.class, RFC3339InstantDeserializer.ZONED_DATE_TIME);
}
{{/useJackson3}}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@

package {{invokerPackage}};

{{^useJackson3}}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
{{/useJackson3}}
{{#useJackson3}}
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.JsonMapper;
import tools.jackson.databind.ObjectMapper;
{{/useJackson3}}
{{#openApiNullable}}
{{^useJackson3}}
import org.openapitools.jackson.nullable.JsonNullableModule;
{{/useJackson3}}
{{#useJackson3}}
import org.openapitools.jackson.nullable.JsonNullableJackson3Module;
{{/useJackson3}}
{{/openApiNullable}}
{{#generateClientAsBean}}
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -27,8 +39,14 @@ import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.reactive.ClientHttpRequest;
{{^useJackson3}}
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
{{/useJackson3}}
{{#useJackson3}}
import org.springframework.http.codec.json.JacksonJsonDecoder;
import org.springframework.http.codec.json.JacksonJsonEncoder;
{{/useJackson3}}
{{#generateClientAsBean}}
import org.springframework.stereotype.Component;
{{/generateClientAsBean}}
Expand Down Expand Up @@ -130,11 +148,11 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
}

public ApiClient(ObjectMapper mapper, DateFormat format) {
this(buildWebClient(mapper.copy()), format);
this(buildWebClient(mapper{{^useJackson3}}.copy(){{/useJackson3}}), format);
}

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

protected ApiClient(WebClient webClient, DateFormat format) {
Expand All @@ -154,6 +172,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
if (null == dateFormat) {
dateFormat = createDefaultDateFormat();
}
{{^useJackson3}}
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(dateFormat);
mapper.registerModule(new JavaTimeModule());
Expand All @@ -163,6 +182,17 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
mapper.registerModule(jnm);
{{/openApiNullable}}
return mapper;
{{/useJackson3}}
{{#useJackson3}}
return JsonMapper
.builder()
.setDateFormat(dateFormat)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}})
{{#openApiNullable}}
.addModule(new JsonNullableJackson3Module())
{{/openApiNullable}}
.build();
{{/useJackson3}}
}

protected void init() {
Expand All @@ -185,8 +215,14 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
ExchangeStrategies strategies = ExchangeStrategies
.builder()
.codecs(clientDefaultCodecsConfigurer -> {
{{^useJackson3}}
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON));
{{/useJackson3}}
{{#useJackson3}}
clientDefaultCodecsConfigurer.defaultCodecs().jacksonJsonEncoder(new JacksonJsonEncoder(mapper));
clientDefaultCodecsConfigurer.defaultCodecs().jacksonJsonDecoder(new JacksonJsonDecoder(mapper));
{{/useJackson3}}
}).build();
WebClient.Builder webClientBuilder = WebClient.builder().exchangeStrategies(strategies);
return webClientBuilder;
Expand Down Expand Up @@ -456,20 +492,30 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
if (value instanceof Collection) {
valueCollection = (Collection<?>) value;
} else {
{{^useJackson3}}
try {
return parameterToMultiValueMap(collectionFormat, name, objectMapper.writeValueAsString(value));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
{{/useJackson3}}
{{#useJackson3}}
return parameterToMultiValueMap(collectionFormat, name, objectMapper.writeValueAsString(value));
{{/useJackson3}}
}

List<String> values = new ArrayList<>();
for(Object o : valueCollection) {
{{^useJackson3}}
try {
values.add(objectMapper.writeValueAsString(o));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
{{/useJackson3}}
{{#useJackson3}}
values.add(objectMapper.writeValueAsString(o));
{{/useJackson3}}
}
return parameterToMultiValueMap(collectionFormat, name, "[" + StringUtils.collectionToDelimitedString(values, collectionFormat.separator) + "]");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ ext {
swagger_annotations_version = "2.2.9"
{{/swagger2AnnotationLibrary}}
{{#useJakartaEe}}
{{^useJackson3}}
spring_boot_version = "3.2.12"
{{/useJackson3}}
{{#useJackson3}}
spring_boot_version = "4.0.3"
{{/useJackson3}}
jakarta_annotation_version = "2.1.1"
beanvalidation_version = "3.0.2"
reactor_version = "3.5.12"
Expand All @@ -144,8 +149,13 @@ ext {
reactor_version = "3.4.34"
reactor_netty_version = "1.2.8"
{{/useJakartaEe}}
{{^useJackson3}}
jackson_version = "2.19.2"
jackson_databind_version = "2.19.2"
{{/useJackson3}}
{{#useJackson3}}
jackson_version = "3.1.0"
jackson_annotations_version = "2.19.2"
{{/useJackson3}}
{{#openApiNullable}}
jackson_databind_nullable_version = "0.2.9"
{{/openApiNullable}}
Expand All @@ -166,17 +176,25 @@ dependencies {
implementation "io.projectreactor:reactor-core:$reactor_version"
implementation "org.springframework.boot:spring-boot-starter-webflux:$spring_boot_version"
implementation "io.projectreactor.netty:reactor-netty-http:$reactor_netty_version"
{{^useJackson3}}
implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_databind_version"
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
{{/useJackson3}}
{{#useJackson3}}
implementation "tools.jackson.core:jackson-core:$jackson_version"
implementation "tools.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
implementation "tools.jackson.core:jackson-databind:$jackson_version"
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_annotations_version"
{{/useJackson3}}
{{#openApiNullable}}
implementation "org.openapitools:jackson-databind-nullable:$jackson_databind_nullable_version"
{{/openApiNullable}}
{{#joda}}
implementation "joda-time:joda-time:$jodatime_version"
{{/joda}}
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
implementation "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version"
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version"
}
Loading