[kotlin] Serialize inline number enums by value with kotlinx_serialization

FI
8h ago
+157-1

Description:

With serializationLibrary=kotlinx_serialization, a number enum declared inline on a property (plays: { type: integer, enum: [1, 2] }) was written as the JSON string "1" and could not be read from the number 1. The same enum declared as its own schema already works, because #21746 gave enum_class.mustache a value serializer for every non-string enum. data_class.mustache only added (with = …Serializer::class) and the serializer object under enumUnknownDefaultCase.

kotlin-client/data_class.mustache now does the same as enum_class.mustache for inline enums: when enumUnknownDefaultCase is off and the enum is not a string or container, the nested enum gets @Serializable(with = <Name>Serializer::class) and an internal object <Name>Serializer that reads and writes value with the enum's type serializer. The enumUnknownDefaultCase path is unchanged. The needed kotlinx.serialization imports are added when the model has enums. The serializer object is not emitted for multiplatform, which is out of scope here: inline number enums there still behave as before. Number (BigDecimal) enums keep the previous plain @Serializable path; supporting them properly needs a toEnumValue change across Kotlin generators, so I left that out of scope.

I added two tests in KotlinClientCodegenModelTest with 3_0/kotlin/issue25459-kotlinx-inline-numeric-enum.yaml, one for @Serializable and one for serializableModel=true (@KSerializable). Both fail without the change. Regenerating the kotlinx samples adds the four imports to Pet.kt and Order.kt in kotlin-jvm-ktor-kotlinx_serialization and kotlin-retrofit2-kotlinx_serialization. Their inline enums are strings, so nothing else in them changes.

Fixes #25459

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples (see below for what I ran instead):
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

What I ran on this branch (macOS, JDK 21, Maven wrapper):

  • ./mvnw -pl modules/openapi-generator -am test -Dtest=KotlinClientCodegenModelTest passes (71 tests). With data_class.mustache put back to master, the two new tests fail.

  • ./mvnw -pl modules/openapi-generator -am test -Dtest=KotlinClientCodegenModelTest,KotlinClientCodegenApiTest,KotlinModelCodegenTest passes (126 tests, 0 failures, 4 skipped).

  • Both changed samples compile with their own Gradle wrapper (./gradlew compileKotlin).

  • I generated a jvm-retrofit2 kotlinx client from the issue spec and added a small round-trip test locally (not committed). With this change, Recording(plays = _1, speed = _1) encodes as {"plays":1,"speed":1} and {"plays":2,"speed":2} decodes. With master templates it encodes {"plays":"1","speed":1} and decoding fails with JsonDecodingException. The same test passes with serializableModel=true.

  • ./bin/generate-samples.sh with the ten configs that set serializationLibrary: kotlinx_serialization, plus kotlin-multiplatform.yaml and kotlin-enum-integers-multiplatform.yaml, exits 0. Only the four files above change.

I did not run the full ./mvnw clean package, the other sample configs or ./bin/utils/export_docs_generators.sh, so the second box is unchecked. No option or docs text changed.

cc @karismann @Zomzog @andrewemery @4brunu @yutaka0m @stefankoppier @e5l


Summary by cubic

Fixes inline number enum serialization in Kotlin clients using kotlinx_serialization. An enum declared inline on a property (enum: [1, 2]) was written as the JSON string "1" and failed to decode from the number 1; it now serializes and deserializes by value, matching standalone enums (which already had a value serializer from #21746).

  • Relevant only when enumUnknownDefaultCase is off; that code path is unchanged.
  • multiplatform output is unchanged.
  • Regenerated samples gained only the new kotlinx.serialization imports because their inline enums are strings.