From 8e179bdba2daf8d319b4c60727ce78026be5a424 Mon Sep 17 00:00:00 2001 From: Ramon Onis Date: Fri, 12 Dec 2025 14:13:02 +0100 Subject: [PATCH 1/4] fix(kotlin): add JsonCreator/JsonValue for numeric enums --- .../kotlin-client/enum_class.mustache | 11 +++++-- .../kotlin/KotlinClientCodegenModelTest.java | 26 ++++++++++++++++ .../issue22534-kotlin-numeric-enum.yaml | 31 +++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/enum_class.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/enum_class.mustache index 16326a77fe02..c34efdd961eb 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-client/enum_class.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-client/enum_class.mustache @@ -7,10 +7,13 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass {{/moshi}} {{#jackson}} +import com.fasterxml.jackson.annotation.JsonCreator {{#enumUnknownDefaultCase}} import com.fasterxml.jackson.annotation.JsonEnumDefaultValue {{/enumUnknownDefaultCase}} import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonValue +import kotlin.jvm.JvmStatic {{/jackson}} {{#kotlinx_serialization}} import kotlinx.serialization.SerialName @@ -52,7 +55,7 @@ import kotlinx.serialization.* @JsonClass(generateAdapter = false) {{/moshi}} {{/multiplatform}} -{{#nonPublicApi}}internal {{/nonPublicApi}}{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}enum class {{classname}}({{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}val value: {{{dataType}}}) { +{{#nonPublicApi}}internal {{/nonPublicApi}}{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}enum class {{classname}}({{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}{{#jackson}}@get:JsonValue {{/jackson}}val value: {{{dataType}}}) { {{#allowableValues}}{{#enumVars}} {{^multiplatform}} {{#moshi}} @@ -103,7 +106,9 @@ import kotlinx.serialization.* /** * Returns a valid [{{classname}}] for [data], null otherwise. */ - {{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}? = data?.let { + {{#jackson}}@JvmStatic + @JsonCreator + {{/jackson}}{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}? = data?.let { val normalizedData = "$it".lowercase() values().firstOrNull { value -> it == value || normalizedData == "$value".lowercase() @@ -146,4 +151,4 @@ internal object {{classname}}Serializer : KSerializer<{{classname}}> { } } {{/isString}}{{/enumUnknownDefaultCase}} -{{/kotlinx_serialization}} \ No newline at end of file +{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java index fe164556ce0f..38b217a4deaa 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java @@ -688,6 +688,32 @@ public void testIntArrayToEnum() throws IOException { TestUtils.assertFileContains(modelKt, "enum class DaysOfWeek(val value: kotlin.Int)"); } + @Test + public void testJacksonNumericEnumsUseJsonCreator() throws IOException { + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("kotlin") + .setLibrary("jvm-retrofit2") + .setAdditionalProperties(new HashMap<>() {{ + put(CodegenConstants.SERIALIZATION_LIBRARY, "jackson"); + put(CodegenConstants.MODEL_PACKAGE, "model"); + }}) + .setInputSpec("src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(); + + generator.opts(clientOptInput).generate(); + + final Path enumKt = Paths.get(output + "/src/main/kotlin/model/ExampleNumericEnum.kt"); + + TestUtils.assertFileContains(enumKt, "@get:JsonValue"); + TestUtils.assertFileContains(enumKt, "@JsonCreator"); + } + @Test(description = "convert an empty model to object") public void emptyModelKotlinxSerializationTest() throws IOException { final Schema schema = new ObjectSchema() diff --git a/modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml b/modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml new file mode 100644 index 000000000000..717f23afc521 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + title: 'Issue 5077 Jackson numeric enum regression' + version: latest +paths: + '/example': + get: + operationId: getExample + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/ExampleModel' +components: + schemas: + ExampleModel: + required: + - source + type: object + properties: + source: + $ref: '#/components/schemas/ExampleNumericEnum' + ExampleNumericEnum: + type: integer + format: int32 + enum: + - -1 + - 0 + - 1 From 15bd0cbc19b691e289d60d8798fa08803d622814 Mon Sep 17 00:00:00 2001 From: Ramon Onis Date: Fri, 12 Dec 2025 14:26:51 +0100 Subject: [PATCH 2/4] Regenerate samples --- .../kotlin/org/openapitools/client/models/StringEnumRef.kt | 7 ++++++- .../kotlin/org/openapitools/client/models/StringEnumRef.kt | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/samples/client/echo_api/kotlin-jvm-spring-3-restclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt b/samples/client/echo_api/kotlin-jvm-spring-3-restclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt index 93e12dd2ad7c..7f3601230336 100644 --- a/samples/client/echo_api/kotlin-jvm-spring-3-restclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt +++ b/samples/client/echo_api/kotlin-jvm-spring-3-restclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt @@ -16,8 +16,11 @@ package org.openapitools.client.models +import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonEnumDefaultValue import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonValue +import kotlin.jvm.JvmStatic /** * @@ -25,7 +28,7 @@ import com.fasterxml.jackson.annotation.JsonProperty * Values: success,failure,unclassified,unknown_default_open_api */ -enum class StringEnumRef(val value: kotlin.String) { +enum class StringEnumRef(@get:JsonValue val value: kotlin.String) { @JsonProperty(value = "success") success("success"), @@ -57,6 +60,8 @@ enum class StringEnumRef(val value: kotlin.String) { /** * Returns a valid [StringEnumRef] for [data], null otherwise. */ + @JvmStatic + @JsonCreator fun decode(data: kotlin.Any?): StringEnumRef? = data?.let { val normalizedData = "$it".lowercase() values().firstOrNull { value -> diff --git a/samples/client/echo_api/kotlin-jvm-spring-3-webclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt b/samples/client/echo_api/kotlin-jvm-spring-3-webclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt index 93e12dd2ad7c..7f3601230336 100644 --- a/samples/client/echo_api/kotlin-jvm-spring-3-webclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt +++ b/samples/client/echo_api/kotlin-jvm-spring-3-webclient/src/main/kotlin/org/openapitools/client/models/StringEnumRef.kt @@ -16,8 +16,11 @@ package org.openapitools.client.models +import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonEnumDefaultValue import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonValue +import kotlin.jvm.JvmStatic /** * @@ -25,7 +28,7 @@ import com.fasterxml.jackson.annotation.JsonProperty * Values: success,failure,unclassified,unknown_default_open_api */ -enum class StringEnumRef(val value: kotlin.String) { +enum class StringEnumRef(@get:JsonValue val value: kotlin.String) { @JsonProperty(value = "success") success("success"), @@ -57,6 +60,8 @@ enum class StringEnumRef(val value: kotlin.String) { /** * Returns a valid [StringEnumRef] for [data], null otherwise. */ + @JvmStatic + @JsonCreator fun decode(data: kotlin.Any?): StringEnumRef? = data?.let { val normalizedData = "$it".lowercase() values().firstOrNull { value -> From 83f68f4b1eb1b4aaa6e9870be65515a1001abbf5 Mon Sep 17 00:00:00 2001 From: Ramon Onis Date: Fri, 12 Dec 2025 14:31:02 +0100 Subject: [PATCH 3/4] Update title in numeric enum YAML file --- .../resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml b/modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml index 717f23afc521..adae27acb70f 100644 --- a/modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - title: 'Issue 5077 Jackson numeric enum regression' + title: 'Issue 22534 Jackson numeric enum regression' version: latest paths: '/example': From 7f53f2709ded035082e7560bcf6fb1a2b64b4a1a Mon Sep 17 00:00:00 2001 From: Ramon Onis Date: Fri, 12 Dec 2025 14:31:34 +0100 Subject: [PATCH 4/4] rename test --- .../codegen/kotlin/KotlinClientCodegenModelTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java index 38b217a4deaa..a01c5e7b910d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinClientCodegenModelTest.java @@ -689,7 +689,7 @@ public void testIntArrayToEnum() throws IOException { } @Test - public void testJacksonNumericEnumsUseJsonCreator() throws IOException { + public void testJacksonEnumsUseJsonCreator() throws IOException { File output = Files.createTempDirectory("test").toFile(); output.deleteOnExit();