Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2a9e1e6
add jackson 3 support to java native
thorstenhirsch Feb 27, 2026
8f7a982
upgrade to jackson 3.0.4, fix whitespaces
thorstenhirsch Feb 27, 2026
a8fb300
remove importMapping.put section
thorstenhirsch Mar 1, 2026
b69df0d
require Java 17 for Jackson 3
thorstenhirsch Mar 1, 2026
caca305
fix cubic-dev-ai issues, fix unit tests
thorstenhirsch Mar 1, 2026
4e39fad
fix more cubic-dev-ai findings, includes a solution for missing jacks…
thorstenhirsch Mar 1, 2026
f13f114
use SerializationContext instead of SerializerProvider
thorstenhirsch Mar 3, 2026
a1e24d1
use JsonMapper instead of ObjectMapper for Jackson 3
thorstenhirsch Mar 3, 2026
e8453d3
also fix Mapper in JSON.java for Jackson 3
thorstenhirsch Mar 3, 2026
fd4a67c
update samples
thorstenhirsch Mar 3, 2026
19d1296
fix mapper.copy()
thorstenhirsch Mar 3, 2026
f118f3f
add JacksonException handling
thorstenhirsch Mar 3, 2026
8091162
remove IOException where not thrown anymore
thorstenhirsch Mar 3, 2026
45dc531
Merge branch 'master' into feature/jackson3-native
thorstenhirsch Mar 3, 2026
bf23069
Update jackson_annotations_version in build.gradle.mustache
thorstenhirsch Mar 3, 2026
786e186
update samples after rebase
thorstenhirsch Mar 3, 2026
d4bcb59
fix FakeApi integration test
thorstenhirsch Mar 4, 2026
9d5e569
add native-jackson3 to samples-java-client-jdk17 workflow
thorstenhirsch Mar 4, 2026
3bc4c27
Merge branch 'master' into feature/jackson3-native
thorstenhirsch Mar 4, 2026
fbf011e
fix compile errors
thorstenhirsch Mar 4, 2026
52a5a6b
update samples and docs
thorstenhirsch Mar 4, 2026
cf4a519
update Jackson to v3.1.0, use {{jacksonPackage}}
thorstenhirsch Mar 4, 2026
5d2fb18
update docs and samples
thorstenhirsch Mar 4, 2026
c93e857
Update Jackson 3 compatibility note in documentation
thorstenhirsch Mar 5, 2026
b0f9078
Merge branch 'OpenAPITools:master' into feature/jackson3-native
thorstenhirsch Mar 5, 2026
e801e42
update the correct documentation
thorstenhirsch Mar 5, 2026
4ce631a
Update docs/generators/java-microprofile.md once again
thorstenhirsch Mar 5, 2026
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
  •  
  •  
  •  
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 @@ -6,6 +6,7 @@ on:
- samples/client/petstore/java/resttemplate-jakarta/**
- samples/client/petstore/java/webclient-jakarta/**
- samples/client/petstore/java/restclient-*/**
- samples/client/petstore/java/native-jackson3/**
- samples/client/others/java/webclient-sealedInterface/**
- samples/client/others/java/webclient-sealedInterface_3_1/**
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
Expand All @@ -15,6 +16,7 @@ on:
- samples/client/petstore/java/resttemplate-jakarta/**
- samples/client/petstore/java/webclient-jakarta/**
- samples/client/petstore/java/restclient-*/**
- samples/client/petstore/java/native-jackson3/**
- samples/client/others/java/webclient-sealedInterface/**
- samples/client/others/java/webclient-sealedInterface_3_1/**
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
Expand All @@ -37,6 +39,7 @@ jobs:
- samples/client/petstore/java/restclient-swagger2
- samples/client/petstore/java/restclient-useSingleRequestParameter
- samples/client/petstore/java/restclient-useSingleRequestParameter-static
- samples/client/petstore/java/native-jackson3
- samples/client/others/java/webclient-sealedInterface
- samples/client/others/java/webclient-sealedInterface_3_1
- samples/client/petstore/java/webclient-useSingleRequestParameter
Expand Down
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i did a test with mvn integration-test -f samples/client/petstore/java/native-jackson3/pom.xml but resulted in errors, e..g

[ERROR] /C:/Users/User/code/openapi-generator/samples/client/petstore/java/native-jackson3/src/test/java/org/openapitools/client/model/EnumTestTest.java:[21,41] package org.openapitools.jackson.nullable does not exist
[ERROR] /C:/Users/User/code/openapi-generator/samples/client/petstore/java/native-jackson3/src/test/java/org/openapitools/client/model/EnumTestTest.java:[27,41] package org.openapitools.jackson.nullable does not exist
[ERROR] /C:/Users/User/code/openapi-generator/samples/client/petstore/java/native-jackson3/src/test/java/org/openapitools/client/api/FakeApiTest.java:[320,16] cannot find symbol
[ERROR]   symbol:   class APItestGroupParametersRequest
[ERROR]   location: class org.openapitools.client.api.FakeApi
[ERROR] /C:/Users/User/code/openapi-generator/samples/client/petstore/java/native-jackson3/src/test/java/org/openapitools/client/api/FakeApiTest.java:[320,64] cannot find symbol

can you please take a look when you've time?

also please add this folder to the github workflow: https://github.com/OpenAPITools/openapi-generator/blob/master/.github/workflows/samples-java-client-jdk17.yaml

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"
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|Use Jackson 3 instead of Jackson 2. Supported for 'native' library (requires Java 17+) and for 'restclient' library (requires useSpringBoot4=true). Incompatible with 'openApiNullable' (auto-disabled when both are enabled).| |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|Use Jackson 3 instead of Jackson 2. Supported for 'native' library (requires Java 17+) and for 'restclient' library (requires useSpringBoot4=true).| |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 @@ -108,6 +108,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 All @@ -119,7 +120,6 @@ public class JavaClientCodegen extends AbstractJavaCodegen
public static final String SERIALIZATION_LIBRARY_JSONB = "jsonb";

public static final String USE_SPRING_BOOT4 = "useSpringBoot4";
public static final String USE_JACKSON_3 = "useJackson3";
private static final String JACKSON2_PACKAGE = "com.fasterxml.jackson";
private static final String JACKSON3_PACKAGE = "tools.jackson";
private static final String JACKSON_PACKAGE = "jacksonPackage";
Expand Down Expand Up @@ -158,14 +158,14 @@ 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;
/**
* Serialization library.
*/
@Getter protected String serializationLibrary = null;
@Getter @Setter protected boolean useSpringBoot4 = false;
@Getter @Setter protected boolean useJackson3 = false;
@Setter protected boolean useOneOfDiscriminatorLookup = false; // use oneOf discriminator's mapping for model lookup
protected String rootJavaEEPackage;
protected Map<String, MpRestClientVersion> mpRestClientVersions = new LinkedHashMap<>();
Expand Down Expand Up @@ -270,6 +270,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. Supported for 'native' library (requires Java 17+) and for 'restclient' library (requires useSpringBoot4=true).", 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 @@ -307,7 +308,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
// 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,11 +379,17 @@ 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 (useJackson3 && libRestClient && !useSpringBoot4) {
throw new IllegalArgumentException("useJackson3 for the restclient library requires useSpringBoot4=true");
} else if (useJackson3 && !libNative && !libRestClient) {
LOGGER.warn("useJackson3 is only supported for 'native' and 'restclient' libraries. Disabling useJackson3.");
useJackson3 = false;
additionalProperties.put(USE_JACKSON_3, false);
}
if(isUseJackson3() && isOpenApiNullable()){
throw new IllegalArgumentException("openApiNullable cannot be set with useJackson3");
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);
}

if(this.useJackson3){
Expand Down Expand Up @@ -814,7 +821,7 @@ public void processOpts() {
additionalProperties.remove(SERIALIZATION_LIBRARY_GSON);
additionalProperties.remove(SERIALIZATION_LIBRARY_JSONB);
supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", invokerFolder, "RFC3339DateFormat.java"));
if (!useJackson3) {
if (!useJackson3 || libNative) {
supportingFiles.add(new SupportingFile("RFC3339InstantDeserializer.mustache", invokerFolder, "RFC3339InstantDeserializer.java"));
supportingFiles.add(new SupportingFile("RFC3339JavaTimeModule.mustache", invokerFolder, "RFC3339JavaTimeModule.java"));
}
Expand Down Expand Up @@ -1061,7 +1068,9 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
model.imports.add("JsonProperty");
model.imports.add("JsonValue");
model.imports.add("JsonInclude");
model.imports.add("JsonTypeName");
if (!useJackson3) {
model.imports.add("JsonTypeName");
}
}
if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_GSON)) {
model.imports.add("SerializedName");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
{{>licenseInfo}}
package {{invokerPackage}};

import {{jacksonPackage}}.databind.util.StdDateFormat;

import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Date;
import java.text.DecimalFormat;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import {{jacksonPackage}}.databind.util.StdDateFormat;

{{>generatedAnnotation}}

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;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
{{#useJackson3}}
import tools.jackson.core.JacksonException;
{{/useJackson3}}
import {{jacksonPackage}}.core.JsonParser;
import {{jacksonPackage}}.databind.DeserializationContext;
{{^useJackson3}}
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
{{/useJackson3}}
{{#useJackson3}}
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,10 @@ import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;

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

{{>generatedAnnotation}}

Expand All @@ -15,8 +17,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 +33,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,13 +2,20 @@
package {{invokerPackage}};

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import {{jacksonPackage}}.databind.DeserializationFeature;
import {{jacksonPackage}}.databind.ObjectMapper;
{{^useJackson3}}
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
{{#openApiNullable}}
import org.openapitools.jackson.nullable.JsonNullableModule;
{{/openApiNullable}}
{{/useJackson3}}
{{#useJackson3}}
import tools.jackson.databind.cfg.DateTimeFeature;
import tools.jackson.databind.cfg.EnumFeature;
import tools.jackson.databind.json.JsonMapper;
{{/useJackson3}}

import java.io.InputStream;
import java.io.IOException;
Expand Down Expand Up @@ -205,6 +212,7 @@ public class ApiClient {
}

public static ObjectMapper createDefaultObjectMapper() {
{{^useJackson3}}
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Expand All @@ -219,6 +227,19 @@ public class ApiClient {
{{/openApiNullable}}
mapper.registerModule(new RFC3339JavaTimeModule());
return mapper;
{{/useJackson3}}
{{#useJackson3}}
return JsonMapper.builder()
.changeDefaultPropertyInclusion(v -> v.withValueInclusion(JsonInclude.Include.NON_NULL))
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)
.disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
.enable(EnumFeature.WRITE_ENUMS_USING_TO_STRING)
.enable(EnumFeature.READ_ENUMS_USING_TO_STRING)
.disable(DateTimeFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.addModule(new RFC3339JavaTimeModule())
.build();
{{/useJackson3}}
}

protected final String getDefaultBaseUri() {
Expand Down Expand Up @@ -273,12 +294,17 @@ public class ApiClient {
}

/**
* Get a copy of the current {@link ObjectMapper}.
* Get {{^useJackson3}}a copy of {{/useJackson3}}the current {@link ObjectMapper}.
*
* @return A copy of the current object mapper.
* @return {{^useJackson3}}A copy of the current{{/useJackson3}}{{#useJackson3}}The current{{/useJackson3}} object mapper.
*/
public ObjectMapper getObjectMapper() {
{{^useJackson3}}
return mapper.copy();
{{/useJackson3}}
{{#useJackson3}}
return mapper;
{{/useJackson3}}
}

/**
Expand Down
Loading
Loading