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
3 changes: 3 additions & 0 deletions .github/workflows/samples-java-client-jdk17.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
- samples/client/others/java/webclient-sealedInterface/**
- samples/client/others/java/webclient-sealedInterface_3_1/**
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
- samples/client/petstore/java/webclient-jackson3/**
- samples/client/others/java/restclient-enum-in-multipart/**
pull_request:
paths:
Expand All @@ -18,6 +19,7 @@ on:
- samples/client/others/java/webclient-sealedInterface/**
- samples/client/others/java/webclient-sealedInterface_3_1/**
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
- samples/client/petstore/java/webclient-jackson3/**
- samples/client/others/java/restclient-enum-in-multipart/**
jobs:
build:
Expand All @@ -40,6 +42,7 @@ jobs:
- samples/client/others/java/webclient-sealedInterface
- samples/client/others/java/webclient-sealedInterface_3_1
- samples/client/petstore/java/webclient-useSingleRequestParameter
- samples/client/petstore/java/webclient-jackson3
- samples/client/others/java/restclient-enum-in-multipart
steps:
- uses: actions/checkout@v5
Expand Down
12 changes: 12 additions & 0 deletions bin/configs/java-webclient-jackson3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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"
useSpringBoot4: true
useJackson3: true
useJakartaEe: true
openApiNullable: false
2 changes: 1 addition & 1 deletion docs/generators/java-microprofile.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +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|Set it in order to use jackson 3 dependencies (only allowed when `useSpringBoot4` is set and incompatible with `openApiNullable`).| |false|
|useJackson3|Set it in order to use jackson 3 dependencies (only allowed when `useSpringBoot4` is set and incompatible with `openApiNullable`). For 'java' generator: 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
2 changes: 1 addition & 1 deletion docs/generators/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +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|Set it in order to use jackson 3 dependencies (only allowed when `useSpringBoot4` is set and incompatible with `openApiNullable`).| |false|
|useJackson3|Set it in order to use jackson 3 dependencies (only allowed when `useSpringBoot4` is set and incompatible with `openApiNullable`). For 'java' generator: 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 @@ -307,7 +307,7 @@ public JavaClientCodegen() {
serializationLibrary.setEnum(serializationOptions);
cliOptions.add(serializationLibrary);
cliOptions.add(CliOption.newBoolean(USE_SPRING_BOOT4, "Generate code and provide dependencies for use with Spring Boot 4.x.", useSpringBoot4));
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
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
// inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values,
// and the discriminator mapping schemas in the OAS document.
this.setLegacyDiscriminatorBehavior(false);
Expand Down Expand Up @@ -378,16 +378,20 @@ public void processOpts() {
convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, this::setUseOneOfDiscriminatorLookup);
convertPropertyToBooleanAndWriteBack(USE_JACKSON_3, this::setUseJackson3);
convertPropertyToBooleanAndWriteBack(USE_SPRING_BOOT4, this::setUseSpringBoot4);
if(isUseJackson3() && !isUseSpringBoot4()){
throw new IllegalArgumentException("useJackson3 is only available with Spring Boot >= 4");
}
if(isUseJackson3() && isOpenApiNullable()){
throw new IllegalArgumentException("openApiNullable cannot be set with useJackson3");

if (isUseJackson3()) {
if (!isUseSpringBoot4()) {
throw new IllegalArgumentException("useJackson3 is only available with Spring Boot >= 4");
}
if (isOpenApiNullable()) {
throw new IllegalArgumentException("openApiNullable cannot be set with useJackson3");
}
}

if(this.useJackson3){
if (isUseJackson3()) {
this.applyJackson3Package();
} else {
}
else {
this.applyJackson2Package();
}

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,23 @@

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.json.JsonMapper;
{{/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 +38,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 @@ -110,15 +127,15 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {

protected final WebClient webClient;
protected final DateFormat dateFormat;
protected final ObjectMapper objectMapper;
protected final {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper;

protected Map<String, Authentication> authentications;


public ApiClient() {
this.dateFormat = createDefaultDateFormat();
this.objectMapper = createDefaultObjectMapper(this.dateFormat);
this.webClient = buildWebClient(this.objectMapper);
this.mapper = createDefaultMapper(this.dateFormat);
this.webClient = buildWebClient(this.mapper);
this.init();
}

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

public ApiClient(ObjectMapper mapper, DateFormat format) {
this(buildWebClient(mapper.copy()), format);
public ApiClient({{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper, DateFormat 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);
public ApiClient(WebClient webClient, {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper, DateFormat format) {
this(Optional.ofNullable(webClient).orElseGet(() -> buildWebClient(mapper{{^useJackson3}}.copy(){{/useJackson3}})), format);
}

protected ApiClient(WebClient webClient, DateFormat format) {
this.webClient = webClient;
this.dateFormat = format;
this.objectMapper = createDefaultObjectMapper(format);
this.mapper = createDefaultMapper(format);
this.init();
}

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

public static ObjectMapper createDefaultObjectMapper(@Nullable DateFormat dateFormat) {
public static {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} createDefaultMapper(@Nullable DateFormat dateFormat) {
if (null == dateFormat) {
dateFormat = createDefaultDateFormat();
}
{{^useJackson3}}
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(dateFormat);
mapper.registerModule(new JavaTimeModule());
Expand All @@ -163,6 +181,14 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
mapper.registerModule(jnm);
{{/openApiNullable}}
return mapper;
{{/useJackson3}}
{{#useJackson3}}
return JsonMapper
.builder()
.defaultDateFormat(dateFormat)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}})
.build();
{{/useJackson3}}
}

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

/**
* Build the WebClientBuilder used to make WebClient.
* @param mapper ObjectMapper used for serialize/deserialize
* @param mapper {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} used for serialize/deserialize
* @return WebClient
*/
public static WebClient.Builder buildWebClientBuilder(ObjectMapper mapper) {
public static WebClient.Builder buildWebClientBuilder({{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper) {
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 All @@ -197,15 +229,15 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
* @return WebClient
*/
public static WebClient.Builder buildWebClientBuilder() {
return buildWebClientBuilder(createDefaultObjectMapper(null));
return buildWebClientBuilder(createDefaultMapper(null));
}

/**
* Build the WebClient used to make HTTP requests.
* @param mapper ObjectMapper used for serialize/deserialize
* @param mapper {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} used for serialize/deserialize
* @return WebClient
*/
public static WebClient buildWebClient(ObjectMapper mapper) {
public static WebClient buildWebClient({{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper) {
return buildWebClientBuilder(mapper).build();
}

Expand All @@ -214,7 +246,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
* @return WebClient
*/
public static WebClient buildWebClient() {
return buildWebClientBuilder(createDefaultObjectMapper(null)).build();
return buildWebClientBuilder(createDefaultMapper(null)).build();
}

/**
Expand Down Expand Up @@ -403,11 +435,11 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
}

/**
* Get the ObjectMapper used to make HTTP requests.
* @return ObjectMapper objectMapper
* Get the {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} used to make HTTP requests.
* @return {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} mapper
*/
public ObjectMapper getObjectMapper() {
return objectMapper;
public {{^useJackson3}}ObjectMapper{{/useJackson3}}{{#useJackson3}}JsonMapper{{/useJackson3}} getMapper() {
return mapper;
}

/**
Expand Down Expand Up @@ -456,20 +488,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));
return parameterToMultiValueMap(collectionFormat, name, mapper.writeValueAsString(value));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
{{/useJackson3}}
{{#useJackson3}}
return parameterToMultiValueMap(collectionFormat, name, mapper.writeValueAsString(value));
{{/useJackson3}}
}

List<String> values = new ArrayList<>();
for(Object o : valueCollection) {
{{^useJackson3}}
try {
values.add(objectMapper.writeValueAsString(o));
values.add(mapper.writeValueAsString(o));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
{{/useJackson3}}
{{#useJackson3}}
values.add(mapper.writeValueAsString(o));
{{/useJackson3}}
}
return parameterToMultiValueMap(collectionFormat, name, "[" + StringUtils.collectionToDelimitedString(values, collectionFormat.separator) + "]");
}
Expand Down
Loading
Loading