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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 11 additions & 0 deletions bin/configs/java-native-jackson3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
generatorName: java
outputDir: samples/client/petstore/java/native-jackson3
library: native
inputSpec: modules/openapi-generator/src/test/resources/3_0/java/native/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml
templateDir: modules/openapi-generator/src/main/resources/Java
additionalProperties:
artifactId: petstore-native-jackson3
hideGenerationTimestamp: "true"
generateBuilders: true
useReflectionEqualsHashCode: "true"
useJackson3: "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' library.| |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' library.| |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' library.", 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,12 @@ 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);
if (useJackson3 && openApiNullable) {
LOGGER.warn("openApiNullable is not supported with useJackson3=true (jackson-databind-nullable has no Jackson 3 release). Disabling openApiNullable.");
openApiNullable = false;
additionalProperties.put(OPENAPI_NULLABLE, false);
}

// add URL query deepObject support to native, apache-httpclient by default
if (!additionalProperties.containsKey(SUPPORT_URL_QUERY)) {
Expand Down
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,18 +14,34 @@ 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.databind.cfg.DateTimeFeature;
import tools.jackson.databind.ext.javatime.deser.InstantDeserializer;
{{/useJackson3}}

{{>generatedAnnotation}}

public class RFC3339InstantDeserializer<T extends Temporal> extends InstantDeserializer<T> {
private static final long serialVersionUID = 1L;
{{^useJackson3}}
private final static boolean DEFAULT_NORMALIZE_ZONE_ID = JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault();
private final static boolean DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
= JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault();
{{/useJackson3}}
{{#useJackson3}}
private final static boolean DEFAULT_NORMALIZE_ZONE_ID = DateTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault();
private final static boolean DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS
= DateTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault();
{{/useJackson3}}

public static final RFC3339InstantDeserializer<Instant> INSTANT = new RFC3339InstantDeserializer<>(
Instant.class, DateTimeFormatter.ISO_INSTANT,
Expand Down Expand Up @@ -84,7 +102,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
@@ -1,6 +1,7 @@
{{>licenseInfo}}
package {{invokerPackage}};

{{^useJackson3}}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -9,6 +10,13 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
{{#openApiNullable}}
import org.openapitools.jackson.nullable.JsonNullableModule;
{{/openApiNullable}}
{{/useJackson3}}
{{#useJackson3}}
import com.fasterxml.jackson.annotation.JsonInclude;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.SerializationFeature;
{{/useJackson3}}

import java.io.InputStream;
import java.io.IOException;
Expand Down Expand Up @@ -213,10 +221,17 @@ public class ApiClient {
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
{{^useJackson3}}
mapper.registerModule(new JavaTimeModule());
{{/useJackson3}}
{{^useJackson3}}
{{#openApiNullable}}
mapper.registerModule(new JsonNullableModule());
{{/openApiNullable}}
{{/useJackson3}}
{{#useJackson3}}
// FIXME: JsonNullableModule is not yet available for Jackson 3
{{/useJackson3}}
mapper.registerModule(new RFC3339JavaTimeModule());
return mapper;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package {{invokerPackage}};

{{^useJackson3}}
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
Expand All @@ -12,6 +13,15 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
{{#joda}}
import com.fasterxml.jackson.datatype.joda.JodaModule;
{{/joda}}
{{/useJackson3}}
{{#useJackson3}}
import com.fasterxml.jackson.annotation.*;
import tools.jackson.databind.*;
import tools.jackson.databind.json.JsonMapper;
{{#joda}}
import tools.jackson.datatype.joda.JodaModule;
{{/joda}}
{{/useJackson3}}
{{#models.0}}
import {{modelPackage}}.*;
{{/models.0}}
Expand All @@ -28,6 +38,7 @@ public class JSON {
private ObjectMapper mapper;

public JSON() {
{{^useJackson3}}
mapper = JsonMapper.builder()
.serializationInclusion(JsonInclude.Include.NON_NULL)
.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS)
Expand All @@ -51,6 +62,29 @@ public class JSON {
JsonNullableModule jnm = new JsonNullableModule();
mapper.registerModule(jnm);
{{/openApiNullable}}
{{/useJackson3}}
{{#useJackson3}}
mapper = JsonMapper.builder()
.serializationInclusion(JsonInclude.Include.NON_NULL)
// Note: MapperFeature.ALLOW_COERCION_OF_SCALARS was removed in Jackson 3
{{#failOnUnknownProperties}}
.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
{{/failOnUnknownProperties}}
{{^failOnUnknownProperties}}
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
{{/failOnUnknownProperties}}
.enable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
.defaultDateFormat(new RFC3339DateFormat())
// Note: JavaTimeModule (jsr310) is built into jackson-databind for Jackson 3 - no explicit registration needed
.build();
{{#joda}}
mapper.registerModule(new JodaModule());
{{/joda}}
// FIXME: JsonNullableModule is not yet available for Jackson 3
{{/useJackson3}}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

Building the API client library requires:

1. Java 11+
{{^useJackson3}}1. Java 11+{{/useJackson3}}{{#useJackson3}}1. Java 17+{{/useJackson3}}
2. Maven/Gradle

## Installation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{{^useJackson3}}
import java.io.IOException;
{{/useJackson3}}
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;

{{^useJackson3}}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -16,6 +19,20 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
{{/useJackson3}}
{{#useJackson3}}
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.DatabindException;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.SerializerProvider;
import tools.jackson.databind.annotation.JsonDeserialize;
import tools.jackson.databind.annotation.JsonSerialize;
import tools.jackson.databind.deser.std.StdDeserializer;
import tools.jackson.databind.ser.std.StdSerializer;
{{/useJackson3}}
import {{invokerPackage}}.ApiClient;
import {{invokerPackage}}.JSON;

Expand All @@ -35,7 +52,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
}

@Override
public void serialize({{classname}} value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
public void serialize({{classname}} value, JsonGenerator jgen, SerializerProvider provider) throws {{^useJackson3}}IOException, JsonProcessingException{{/useJackson3}}{{#useJackson3}}JacksonException{{/useJackson3}} {
jgen.writeObject(value.getActualInstance());
}
}
Expand All @@ -50,7 +67,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
}

@Override
public {{classname}} deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
public {{classname}} deserialize(JsonParser jp, DeserializationContext ctxt) throws {{^useJackson3}}IOException, JsonProcessingException{{/useJackson3}}{{#useJackson3}}JacksonException{{/useJackson3}} {
JsonNode tree = jp.readValueAsTree();

Object deserialized = null;
Expand Down Expand Up @@ -79,19 +96,34 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
}

{{/anyOf}}
{{^useJackson3}}
throw new IOException("Failed deserialization for {{classname}}: no match found");
{{/useJackson3}}
{{#useJackson3}}
throw DatabindException.from(jp, "Failed deserialization for {{classname}}: no match found");
{{/useJackson3}}
}

/**
* Handle deserialization of the 'null' value.
*/
@Override
{{^useJackson3}}
public {{classname}} getNullValue(DeserializationContext ctxt) throws JsonMappingException {
{{/useJackson3}}
{{#useJackson3}}
public {{classname}} getNullValue(DeserializationContext ctxt) {
{{/useJackson3}}
{{#isNullable}}
return null;
{{/isNullable}}
{{^isNullable}}
{{^useJackson3}}
throw new JsonMappingException(ctxt.getParser(), "{{classname}} cannot be null");
{{/useJackson3}}
{{#useJackson3}}
throw DatabindException.from(ctxt.getParser(), "{{classname}} cannot be null");
{{/useJackson3}}
{{/isNullable}}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ import {{invokerPackage}}.Pair;
import {{import}};
{{/imports}}

{{^useJackson3}}
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
{{/useJackson3}}
{{#useJackson3}}
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;
{{/useJackson3}}

{{#useBeanValidation}}
import {{javaxPackage}}.validation.constraints.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ public class Example {
## {{operationId}}WithHttpInfo

{{^vendorExtensions.x-group-parameters}}
> {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}} {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}})
> {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}})
{{/vendorExtensions.x-group-parameters}}
{{#vendorExtensions.x-group-parameters}}
> {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}} {{operationId}}WithHttpInfo({{#hasParams}}{{operationId}}Request{{/hasParams}})
> {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#hasParams}}{{operationId}}Request{{/hasParams}})
{{/vendorExtensions.x-group-parameters}}

{{summary}}{{#notes}}
Expand Down
Loading
Loading