diff --git a/docs/generators/java.md b/docs/generators/java.md index 84b9056b7a0..e484573c4cc 100644 --- a/docs/generators/java.md +++ b/docs/generators/java.md @@ -56,3 +56,4 @@ sidebar_label: java |useReflectionEqualsHashCode|Use org.apache.commons.lang3.builder for equals and hashCode in the models. WARNING: This will fail under a security manager, unless the appropriate permissions are set up correctly and also there's potential performance impact.| |false| |caseInsensitiveResponseHeaders|Make API response's headers case-insensitive. Available on okhttp-gson, jersey2 libraries| |false| |library|library template (sub-template) to use|
**jersey1**
HTTP client: Jersey client 1.19.x. JSON processing: Jackson 2.8.x. Enable Java6 support using '-DsupportJava6=true'. Enable gzip request encoding using '-DuseGzipFeature=true'. IMPORTANT NOTE: jersey 1.x is no longer actively maintained so please upgrade to 'jersey2' or other HTTP libaries instead.
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.8.x
**feign**
HTTP client: OpenFeign 9.x or 10.x. JSON processing: Jackson 2.8.x. To enable OpenFeign 10.x, set the 'feignVersion' option to '10.x'
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 3.x. JSON processing: Gson 2.8.x. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit**
HTTP client: OkHttp 2.x. JSON processing: Gson 2.x (Retrofit 1.9.0). IMPORTANT NOTE: retrofit1.x is no longer actively maintained so please upgrade to 'retrofit2' instead.
**retrofit2**
HTTP client: OkHttp 3.x. JSON processing: Gson 2.x (Retrofit 2.3.0). Enable the RxJava adapter using '-DuseRxJava[2]=true'. (RxJava 1.x or 2.x)
**resttemplate**
HTTP client: Spring RestTemplate 4.x. JSON processing: Jackson 2.8.x
**webclient**
HTTP client: Spring WebClient 5.x. JSON processing: Jackson 2.9.x
**resteasy**
HTTP client: Resteasy client 3.x. JSON processing: Jackson 2.8.x
**vertx**
HTTP client: VertX client 3.x. JSON processing: Jackson 2.8.x
**google-api-client**
HTTP client: Google API client 1.x. JSON processing: Jackson 2.8.x
**rest-assured**
HTTP client: rest-assured : 4.x. JSON processing: Gson 2.x. Only for Java8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.9.x. Only for Java11+
|okhttp-gson| +|serializationLibrary|Serialization library, default depends from the library|
**jackson**
Use Jackson as serialization library
**gson**
Use Gson as serialization library
|null| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index 4faf042518f..fa3893d3219 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -78,6 +78,10 @@ public class JavaClientCodegen extends AbstractJavaCodegen public static final String RETROFIT_2 = "retrofit2"; public static final String VERTX = "vertx"; + public static final String SERIALIZATION_LIBRARY = "serializationLibrary"; + public static final String SERIALIZATION_LIBRARY_GSON = "gson"; + public static final String SERIALIZATION_LIBRARY_JACKSON = "jackson"; + protected String gradleWrapperPackage = "gradle.wrapper"; protected boolean useRxJava = false; protected boolean useRxJava2 = false; @@ -95,6 +99,8 @@ public class JavaClientCodegen extends AbstractJavaCodegen protected boolean useReflectionEqualsHashCode = false; protected boolean caseInsensitiveResponseHeaders = false; protected String authFolder; + protected String serializationLibrary = null; + public JavaClientCodegen() { super(); @@ -128,7 +134,6 @@ public class JavaClientCodegen extends AbstractJavaCodegen cliOptions.add(CliOption.newBoolean(USE_REFLECTION_EQUALS_HASHCODE, "Use org.apache.commons.lang3.builder for equals and hashCode in the models. WARNING: This will fail under a security manager, unless the appropriate permissions are set up correctly and also there's potential performance impact.")); cliOptions.add(CliOption.newBoolean(CASE_INSENSITIVE_RESPONSE_HEADERS, "Make API response's headers case-insensitive. Available on " + OKHTTP_GSON + ", " + JERSEY2 + " libraries")); - supportedLibraries.put(JERSEY1, "HTTP client: Jersey client 1.19.x. JSON processing: Jackson 2.8.x. Enable Java6 support using '-DsupportJava6=true'. Enable gzip request encoding using '-DuseGzipFeature=true'. IMPORTANT NOTE: jersey 1.x is no longer actively maintained so please upgrade to 'jersey2' or other HTTP libaries instead."); supportedLibraries.put(JERSEY2, "HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.8.x"); supportedLibraries.put(FEIGN, "HTTP client: OpenFeign 9.x or 10.x. JSON processing: Jackson 2.8.x. To enable OpenFeign 10.x, set the 'feignVersion' option to '10.x'"); @@ -150,6 +155,12 @@ public class JavaClientCodegen extends AbstractJavaCodegen cliOptions.add(libraryOption); setLibrary(OKHTTP_GSON); + CliOption serializationLibrary = new CliOption(SERIALIZATION_LIBRARY, "Serialization library, default depends from the library"); + Map serializationOptions = new HashMap<>(); + serializationOptions.put(SERIALIZATION_LIBRARY_GSON, "Use Gson as serialization library"); + serializationOptions.put(SERIALIZATION_LIBRARY_JACKSON, "Use Jackson as serialization library"); + serializationLibrary.setEnum(serializationOptions); + cliOptions.add(serializationLibrary); } @Override @@ -284,6 +295,10 @@ public class JavaClientCodegen extends AbstractJavaCodegen "BeanValidationException.java")); } + if (additionalProperties.containsKey(SERIALIZATION_LIBRARY)) { + setSerializationLibrary(additionalProperties.get(SERIALIZATION_LIBRARY).toString()); + } + //TODO: add doc to retrofit1 and feign if (FEIGN.equals(getLibrary()) || RETROFIT_1.equals(getLibrary())) { modelDocTemplateFiles.remove("model_doc.mustache"); @@ -301,7 +316,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen } if (FEIGN.equals(getLibrary())) { - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); supportingFiles.add(new SupportingFile("ParamExpander.mustache", invokerFolder, "ParamExpander.java")); supportingFiles.add(new SupportingFile("EncodingUtils.mustache", invokerFolder, "EncodingUtils.java")); } else if (OKHTTP_GSON.equals(getLibrary()) || StringUtils.isEmpty(getLibrary())) { @@ -316,48 +331,48 @@ public class JavaClientCodegen extends AbstractJavaCodegen // NOTE: below moved to postProcessOpoerationsWithModels //supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.mustache", authFolder, "OAuthOkHttpClient.java")); //supportingFiles.add(new SupportingFile("auth/RetryingOAuth.mustache", authFolder, "RetryingOAuth.java")); - additionalProperties.put("gson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_GSON); } else if (usesAnyRetrofitLibrary()) { supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.mustache", authFolder, "OAuthOkHttpClient.java")); supportingFiles.add(new SupportingFile("CollectionFormats.mustache", invokerFolder, "CollectionFormats.java")); - additionalProperties.put("gson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_GSON); if ("retrofit2".equals(getLibrary()) && !usePlayWS) { supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); } } else if (JERSEY2.equals(getLibrary())) { supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); supportingFiles.add(new SupportingFile("ApiResponse.mustache", invokerFolder, "ApiResponse.java")); - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } else if (NATIVE.equals(getLibrary())) { setJava8Mode(true); additionalProperties.put("java8", "true"); - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } else if (RESTEASY.equals(getLibrary())) { supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } else if (JERSEY1.equals(getLibrary())) { - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } else if (RESTTEMPLATE.equals(getLibrary())) { - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java")); } else if (WEBCLIENT.equals(getLibrary())) { setJava8Mode(true); additionalProperties.put("java8", "true"); - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } else if (VERTX.equals(getLibrary())) { typeMapping.put("file", "AsyncFile"); importMapping.put("AsyncFile", "io.vertx.core.file.AsyncFile"); setJava8Mode(true); additionalProperties.put("java8", "true"); - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); apiTemplateFiles.put("apiImpl.mustache", "Impl.java"); apiTemplateFiles.put("rxApiImpl.mustache", ".java"); supportingFiles.remove(new SupportingFile("manifest.mustache", projectFolder, "AndroidManifest.xml")); } else if (GOOGLE_API_CLIENT.equals(getLibrary())) { - additionalProperties.put("jackson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } else if (REST_ASSURED.equals(getLibrary())) { - additionalProperties.put("gson", "true"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_GSON); additionalProperties.put("convert", new CaseFormatLambda(LOWER_CAMEL, UPPER_UNDERSCORE)); apiTemplateFiles.put("api.mustache", ".java"); supportingFiles.add(new SupportingFile("ResponseSpecBuilders.mustache", invokerFolder, "ResponseSpecBuilders.java")); @@ -415,16 +430,30 @@ public class JavaClientCodegen extends AbstractJavaCodegen supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java")); supportingFiles.add(new SupportingFile("Pair.mustache", invokerFolder, "Pair.java")); - additionalProperties.put("jackson", "true"); - additionalProperties.remove("gson"); + forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON); } - if (additionalProperties.containsKey("jackson") && !NATIVE.equals(getLibrary())) { - supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", invokerFolder, "RFC3339DateFormat.java")); - if ("threetenbp".equals(dateLibrary) && !usePlayWS) { - supportingFiles.add(new SupportingFile("CustomInstantDeserializer.mustache", invokerFolder, "CustomInstantDeserializer.java")); - } + if(getSerializationLibrary() == null) { + LOGGER.info("No serializationLibrary configured, using '"+SERIALIZATION_LIBRARY_GSON+"' as fallback"); + setSerializationLibrary(SERIALIZATION_LIBRARY_GSON); } + if(SERIALIZATION_LIBRARY_JACKSON.equals(getSerializationLibrary())) { + additionalProperties.put(SERIALIZATION_LIBRARY_JACKSON, "true"); + additionalProperties.remove(SERIALIZATION_LIBRARY_GSON); + if (!NATIVE.equals(getLibrary())) { + supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", invokerFolder, "RFC3339DateFormat.java")); + if ("threetenbp".equals(dateLibrary) && !usePlayWS) { + supportingFiles.add(new SupportingFile("CustomInstantDeserializer.mustache", invokerFolder, "CustomInstantDeserializer.java")); + } + } + } else if (SERIALIZATION_LIBRARY_GSON.equals(getSerializationLibrary())) { + additionalProperties.put(SERIALIZATION_LIBRARY_GSON, "true"); + additionalProperties.remove(SERIALIZATION_LIBRARY_JACKSON); + } else { + additionalProperties.remove(SERIALIZATION_LIBRARY_JACKSON); + additionalProperties.remove(SERIALIZATION_LIBRARY_GSON); + } + } private boolean usesAnyRetrofitLibrary() { @@ -581,12 +610,12 @@ public class JavaClientCodegen extends AbstractJavaCodegen if (!BooleanUtils.toBoolean(model.isEnum)) { //final String lib = getLibrary(); //Needed imports for Jackson based libraries - if (additionalProperties.containsKey("jackson")) { + if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_JACKSON)) { model.imports.add("JsonProperty"); model.imports.add("JsonValue"); model.imports.add("JsonInclude"); } - if (additionalProperties.containsKey("gson")) { + if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_GSON)) { model.imports.add("SerializedName"); model.imports.add("TypeAdapter"); model.imports.add("JsonAdapter"); @@ -596,7 +625,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen } } else { // enum class //Needed imports for Jackson's JsonCreator - if (additionalProperties.containsKey("jackson")) { + if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_JACKSON)) { model.imports.add("JsonValue"); model.imports.add("JsonCreator"); } @@ -607,7 +636,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen public Map postProcessModelsEnum(Map objs) { objs = super.postProcessModelsEnum(objs); //Needed import for Gson based libraries - if (additionalProperties.containsKey("gson")) { + if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_GSON)) { List> imports = (List>) objs.get("imports"); List models = (List) objs.get("models"); for (Object _mo : models) { @@ -628,7 +657,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen @Override public Map postProcessModels(Map objs) { objs = super.postProcessModels(objs); - if (additionalProperties.containsKey("jackson") && !JERSEY1.equals(getLibrary())) { + if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_JACKSON) && !JERSEY1.equals(getLibrary())) { List> imports = (List>) objs.get("imports"); List models = (List) objs.get("models"); for (Object _mo : models) { @@ -712,6 +741,31 @@ public class JavaClientCodegen extends AbstractJavaCodegen this.caseInsensitiveResponseHeaders = caseInsensitiveResponseHeaders; } + /** + * Serialization library. + * @return 'gson' or 'jackson' + */ + public String getSerializationLibrary() { + return serializationLibrary; + } + + public void setSerializationLibrary(String serializationLibrary) { + if(SERIALIZATION_LIBRARY_JACKSON.equalsIgnoreCase(serializationLibrary)) { + this.serializationLibrary = SERIALIZATION_LIBRARY_JACKSON; + } else if(SERIALIZATION_LIBRARY_GSON.equalsIgnoreCase(serializationLibrary)) { + this.serializationLibrary = SERIALIZATION_LIBRARY_GSON; + } else { + throw new IllegalArgumentException("Unexpected serializationLibrary value: " + serializationLibrary); + } + } + + public void forceSerializationLibrary(String serializationLibrary) { + if((this.serializationLibrary != null) && !this.serializationLibrary.equalsIgnoreCase(serializationLibrary)) { + LOGGER.warn("The configured serializationLibrary '" + this.serializationLibrary + "', is not supported by the library: '" + getLibrary() + "', switching back to: " + serializationLibrary); + } + setSerializationLibrary(serializationLibrary); + } + final private static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application\\/json(;.*)?"); final private static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application\\/vnd.(.*)+json(;.*)?"); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index 04e0506a241..158ff7e24de 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -150,6 +150,7 @@ public class JavaClientCodegenTest { Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.API_PACKAGE), "org.openapitools.client.api"); Assert.assertEquals(codegen.getInvokerPackage(), "org.openapitools.client"); Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.INVOKER_PACKAGE), "org.openapitools.client"); + Assert.assertEquals(codegen.getSerializationLibrary(), JavaClientCodegen.SERIALIZATION_LIBRARY_GSON); } @Test @@ -159,6 +160,7 @@ public class JavaClientCodegenTest { codegen.setModelPackage("xyz.yyyyy.zzzzzzz.model"); codegen.setApiPackage("xyz.yyyyy.zzzzzzz.api"); codegen.setInvokerPackage("xyz.yyyyy.zzzzzzz.invoker"); + codegen.setSerializationLibrary("JACKSON"); codegen.processOpts(); Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP), Boolean.TRUE); @@ -169,6 +171,7 @@ public class JavaClientCodegenTest { Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.API_PACKAGE), "xyz.yyyyy.zzzzzzz.api"); Assert.assertEquals(codegen.getInvokerPackage(), "xyz.yyyyy.zzzzzzz.invoker"); Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.INVOKER_PACKAGE), "xyz.yyyyy.zzzzzzz.invoker"); + Assert.assertEquals(codegen.getSerializationLibrary(), JavaClientCodegen.SERIALIZATION_LIBRARY_GSON); // the library JavaClientCodegen.OKHTTP_GSON only supports GSON } @Test @@ -178,6 +181,8 @@ public class JavaClientCodegenTest { codegen.additionalProperties().put(CodegenConstants.MODEL_PACKAGE, "xyz.yyyyy.zzzzzzz.mmmmm.model"); codegen.additionalProperties().put(CodegenConstants.API_PACKAGE, "xyz.yyyyy.zzzzzzz.aaaaa.api"); codegen.additionalProperties().put(CodegenConstants.INVOKER_PACKAGE, "xyz.yyyyy.zzzzzzz.iiii.invoker"); + codegen.additionalProperties().put(JavaClientCodegen.SERIALIZATION_LIBRARY, "JACKSON"); + codegen.additionalProperties().put(CodegenConstants.LIBRARY, JavaClientCodegen.JERSEY2); codegen.processOpts(); Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP), Boolean.TRUE); @@ -188,6 +193,7 @@ public class JavaClientCodegenTest { Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.API_PACKAGE), "xyz.yyyyy.zzzzzzz.aaaaa.api"); Assert.assertEquals(codegen.getInvokerPackage(), "xyz.yyyyy.zzzzzzz.iiii.invoker"); Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.INVOKER_PACKAGE), "xyz.yyyyy.zzzzzzz.iiii.invoker"); + Assert.assertEquals(codegen.getSerializationLibrary(), JavaClientCodegen.SERIALIZATION_LIBRARY_JACKSON); } @Test