[java] Use @JsonFormat instead of @JsonSerialize for bigDecimalAsString property (#19322)

* [java] Use @JsonFormat instead of @JsonSerialize so that JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN is respected and generates a String using BigDecimal#toPlainString instead of BigDecimal#toString

* Only add Jackson @JsonFormat if properties serializeBigDecimalAsString and jackson are enabled

* Do not verify if jackson is available when mapping imports as the property is not evaluated yet

* Removed unused JsonSerialize and ToStringSerializer
This commit is contained in:
Anderson de Borba 2024-08-28 09:22:13 +01:00 committed by GitHub
parent e69fb86957
commit 85763cdb08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 17 additions and 37 deletions

View File

@ -568,15 +568,16 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
this.sanitizeConfig(); this.sanitizeConfig();
// optional jackson mappings for BigDecimal support // optional jackson mappings for BigDecimal support
importMapping.put("ToStringSerializer", "com.fasterxml.jackson.databind.ser.std.ToStringSerializer"); if (serializeBigDecimalAsString) {
importMapping.put("JsonSerialize", "com.fasterxml.jackson.databind.annotation.JsonSerialize"); importMapping.put("JsonFormat", "com.fasterxml.jackson.annotation.JsonFormat");
importMapping.put("JsonDeserialize", "com.fasterxml.jackson.databind.annotation.JsonDeserialize"); }
// imports for pojos // imports for pojos
importMapping.put("ApiModelProperty", "io.swagger.annotations.ApiModelProperty"); importMapping.put("ApiModelProperty", "io.swagger.annotations.ApiModelProperty");
importMapping.put("ApiModel", "io.swagger.annotations.ApiModel"); importMapping.put("ApiModel", "io.swagger.annotations.ApiModel");
importMapping.put("Schema", "io.swagger.v3.oas.annotations.media.Schema"); importMapping.put("Schema", "io.swagger.v3.oas.annotations.media.Schema");
importMapping.put("BigDecimal", "java.math.BigDecimal"); importMapping.put("BigDecimal", "java.math.BigDecimal");
importMapping.put("JsonDeserialize", "com.fasterxml.jackson.databind.annotation.JsonDeserialize");
importMapping.put("JsonProperty", "com.fasterxml.jackson.annotation.JsonProperty"); importMapping.put("JsonProperty", "com.fasterxml.jackson.annotation.JsonProperty");
importMapping.put("JsonSubTypes", "com.fasterxml.jackson.annotation.JsonSubTypes"); importMapping.put("JsonSubTypes", "com.fasterxml.jackson.annotation.JsonSubTypes");
importMapping.put("JsonTypeInfo", "com.fasterxml.jackson.annotation.JsonTypeInfo"); importMapping.put("JsonTypeInfo", "com.fasterxml.jackson.annotation.JsonTypeInfo");
@ -1705,11 +1706,10 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
if (serializeBigDecimalAsString && jackson) { if (serializeBigDecimalAsString && jackson) {
if ("decimal".equals(property.baseType) || "bigdecimal".equalsIgnoreCase(property.baseType)) { if ("decimal".equals(property.baseType) || "bigdecimal".equalsIgnoreCase(property.baseType)) {
// we serialize BigDecimal as `string` to avoid precision loss // we serialize BigDecimal as `string` to avoid precision loss
property.vendorExtensions.put("x-extra-annotation", "@JsonSerialize(using = ToStringSerializer.class)"); property.vendorExtensions.put("x-extra-annotation", "@JsonFormat(shape = JsonFormat.Shape.STRING)");
// this requires some more imports to be added for this model... // this requires some more imports to be added for this model...
model.imports.add("ToStringSerializer"); model.imports.add("JsonFormat");
model.imports.add("JsonSerialize");
} }
} }
@ -1727,7 +1727,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
model.imports.add("Arrays"); model.imports.add("Arrays");
} else if ("set".equals(property.containerType)) { } else if ("set".equals(property.containerType)) {
model.imports.add("LinkedHashSet"); model.imports.add("LinkedHashSet");
if ((!openApiNullable || !property.isNullable) && jackson) { // cannot be wrapped to nullable if ((!openApiNullable || !property.isNullable) && jackson) { // cannot be wrapped to nullable
model.imports.add("JsonDeserialize"); model.imports.add("JsonDeserialize");
property.vendorExtensions.put("x-setter-extra-annotation", "@JsonDeserialize(as = LinkedHashSet.class)"); property.vendorExtensions.put("x-setter-extra-annotation", "@JsonDeserialize(as = LinkedHashSet.class)");
} }

View File

@ -132,9 +132,6 @@ public class JavaCXFClientCodegen extends AbstractJavaCodegen
super.postProcessModelProperty(model, property); super.postProcessModelProperty(model, property);
model.imports.remove("ApiModelProperty"); model.imports.remove("ApiModelProperty");
model.imports.remove("ApiModel"); model.imports.remove("ApiModel");
model.imports.remove("JsonSerialize");
model.imports.remove("ToStringSerializer");
if (jackson) { if (jackson) {
//Add jackson imports when model has inner enum //Add jackson imports when model has inner enum

View File

@ -227,8 +227,7 @@ public class JavaCXFServerCodegen extends AbstractJavaJAXRSServerCodegen
super.postProcessModelProperty(model, property); super.postProcessModelProperty(model, property);
model.imports.remove("ApiModelProperty"); model.imports.remove("ApiModelProperty");
model.imports.remove("ApiModel"); model.imports.remove("ApiModel");
model.imports.remove("JsonSerialize"); model.imports.remove("JsonFormat");
model.imports.remove("ToStringSerializer");
//Add imports for Jackson when model has inner enum //Add imports for Jackson when model has inner enum
if (isJackson()) { if (isJackson()) {

View File

@ -937,8 +937,6 @@ public class JavaClientCodegen extends AbstractJavaCodegen
if (MICROPROFILE.equals(getLibrary())) { if (MICROPROFILE.equals(getLibrary())) {
model.imports.remove("ApiModelProperty"); model.imports.remove("ApiModelProperty");
model.imports.remove("ApiModel"); model.imports.remove("ApiModel");
model.imports.remove("JsonSerialize");
model.imports.remove("ToStringSerializer");
} }
if (!BooleanUtils.toBoolean(model.isEnum)) { if (!BooleanUtils.toBoolean(model.isEnum)) {

View File

@ -371,14 +371,10 @@ public class JavaHelidonClientCodegen extends JavaHelidonCommonCodegen {
if (HELIDON_MP.equals(getLibrary())) { if (HELIDON_MP.equals(getLibrary())) {
model.imports.remove("ApiModelProperty"); model.imports.remove("ApiModelProperty");
model.imports.remove("ApiModel"); model.imports.remove("ApiModel");
model.imports.remove("JsonSerialize");
model.imports.remove("ToStringSerializer");
} else if (HELIDON_SE.equals(getLibrary())) { } else if (HELIDON_SE.equals(getLibrary())) {
// TODO check for SE-specifics // TODO check for SE-specifics
model.imports.remove("ApiModelProperty"); model.imports.remove("ApiModelProperty");
model.imports.remove("ApiModel"); model.imports.remove("ApiModel");
model.imports.remove("JsonSerialize");
model.imports.remove("ToStringSerializer");
} }
if ("set".equals(property.containerType) && !JACKSON.equals(serializationLibrary)) { if ("set".equals(property.containerType) && !JACKSON.equals(serializationLibrary)) {

View File

@ -250,8 +250,6 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
codegenModel.imports.remove("ApiModel"); codegenModel.imports.remove("ApiModel");
} }
if (!jackson) { if (!jackson) {
codegenModel.imports.remove("JsonSerialize");
codegenModel.imports.remove("ToStringSerializer");
codegenModel.imports.remove("JsonValue"); codegenModel.imports.remove("JsonValue");
codegenModel.imports.remove("JsonProperty"); codegenModel.imports.remove("JsonProperty");
} }

View File

@ -435,10 +435,6 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
typeMapping.put("map", "kotlin.collections.MutableMap"); typeMapping.put("map", "kotlin.collections.MutableMap");
} }
// optional jackson mappings for BigDecimal support
importMapping.put("ToStringSerializer", "com.fasterxml.jackson.databind.ser.std.ToStringSerializer");
importMapping.put("JsonSerialize", "com.fasterxml.jackson.databind.annotation.JsonSerialize");
// Swagger import mappings // Swagger import mappings
importMapping.put("ApiModel", "io.swagger.annotations.ApiModel"); importMapping.put("ApiModel", "io.swagger.annotations.ApiModel");
importMapping.put("ApiModelProperty", "io.swagger.annotations.ApiModelProperty"); importMapping.put("ApiModelProperty", "io.swagger.annotations.ApiModelProperty");

View File

@ -2626,7 +2626,7 @@ public class JavaClientCodegenTest {
/** /**
* Regression test for <a href="https://github.com/OpenAPITools/openapi-generator/issues/6496">#6496</a> * Regression test for <a href="https://github.com/OpenAPITools/openapi-generator/issues/6496">#6496</a>
*/ */
@Test void doesNotGenerateJacksonToStringSerializerAnnotation_whenLibraryIsGson_andSerializeBigDecimalAsStringIsTrue() { @Test void doesNotGenerateJacksonJsonFormatAnnotation_whenLibraryIsGson_andSerializeBigDecimalAsStringIsTrue() {
final CodegenConfigurator configurator = new CodegenConfigurator() final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java") .setGeneratorName("java")
.setLibrary(JavaClientCodegen.OKHTTP_GSON) .setLibrary(JavaClientCodegen.OKHTTP_GSON)
@ -2643,10 +2643,8 @@ public class JavaClientCodegenTest {
assertThat(files).hasSize(1).first(FILE).content() assertThat(files).hasSize(1).first(FILE).content()
.doesNotContain( .doesNotContain(
"@JsonDeserialize(as = LinkedHashSet.class)", "@JsonDeserialize(as = LinkedHashSet.class)",
"@JsonSerialize(using = ToStringSerializer.class)", "@JsonFormat(shape = JsonFormat.Shape.STRING)",
"com.fasterxml.jackson.databind.ser.std.ToStringSerializer", "com.fasterxml.jackson.databind.annotation.JsonDeserialize"
"com.fasterxml.jackson.databind.annotation.JsonDeserialize",
"com.fasterxml.jackson.databind.annotation.JsonSerialize"
); );
} }
@ -2654,7 +2652,7 @@ public class JavaClientCodegenTest {
* Test that fix for <a href="https://github.com/OpenAPITools/openapi-generator/issues/6496">#6496</a> has * Test that fix for <a href="https://github.com/OpenAPITools/openapi-generator/issues/6496">#6496</a> has
* no unwanted side effects on the existing feature (Jackson + bigDecimalAsString) * no unwanted side effects on the existing feature (Jackson + bigDecimalAsString)
*/ */
@Test void generatesJacksonToStringSerializerAnnotation_whenLibraryIsJackson_andSerializeBigDecimalAsStringIsTrue() { @Test void generatesJacksonJsonFormatAnnotation_whenLibraryIsJackson_andSerializeBigDecimalAsStringIsTrue() {
final CodegenConfigurator configurator = new CodegenConfigurator() final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java") .setGeneratorName("java")
.setLibrary(JavaClientCodegen.NATIVE) .setLibrary(JavaClientCodegen.NATIVE)
@ -2672,10 +2670,8 @@ public class JavaClientCodegenTest {
assertThat(files).hasSize(1).first(FILE).content() assertThat(files).hasSize(1).first(FILE).content()
.contains( .contains(
"@JsonDeserialize(as = LinkedHashSet.class)", "@JsonDeserialize(as = LinkedHashSet.class)",
"@JsonSerialize(using = ToStringSerializer.class)", "@JsonFormat(shape = JsonFormat.Shape.STRING)",
"com.fasterxml.jackson.databind.ser.std.ToStringSerializer", "com.fasterxml.jackson.databind.annotation.JsonDeserialize"
"com.fasterxml.jackson.databind.annotation.JsonDeserialize",
"com.fasterxml.jackson.databind.annotation.JsonSerialize"
); );
} }

View File

@ -2478,11 +2478,11 @@ public class SpringCodegenTest {
Map<String, File> files = generateFiles(codegen, "src/test/resources/bugs/issue_14252.yaml"); Map<String, File> files = generateFiles(codegen, "src/test/resources/bugs/issue_14252.yaml");
JavaFileAssert.assertThat(files.get("MyResponse.java")) JavaFileAssert.assertThat(files.get("MyResponse.java"))
.hasImports("com.fasterxml.jackson.databind.annotation.JsonSerialize", "com.fasterxml.jackson.databind.ser.std.ToStringSerializer") .hasImports("com.fasterxml.jackson.annotation.JsonFormat")
.assertMethod("getMyPropTypeNumber") .assertMethod("getMyPropTypeNumber")
.assertMethodAnnotations() .assertMethodAnnotations()
.containsWithNameAndAttributes("JsonSerialize", ImmutableMap.of( .containsWithNameAndAttributes("JsonFormat", ImmutableMap.of(
"using", "ToStringSerializer.class" "shape", "JsonFormat.Shape.STRING"
)); ));
} }