Skip to content

[kotlin] Serialize inline number enums by value with kotlinx_serialization - #25473

Open
fixfon wants to merge 2 commits into
OpenAPITools:masterfrom
fixfon:fix/25459-kotlin-inline-number-enum-serializer
Open

fixfon wants to merge 2 commits into
OpenAPITools:masterfrom
fixfon:fix/25459-kotlin-inline-number-enum-serializer

Conversation

@fixfon

@fixfon fixfon commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

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.

Written for commit 310a46e. Summary will update on new commits.

View guided diff

… kotlinx_serialization

Inline enums on a model were annotated with a plain @serializable, so kotlinx.serialization wrote integer and number enums by their @SerialName, as the JSON string "1" instead of the number 1. Inline non-string, non-container enums now get the same value serializer as standalone enums when enumUnknownDefaultCase is off. Models with enums import the serializer types, and multiplatform output is unchanged.

Fixes OpenAPITools#25459

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 7 files

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

…tlinx_serialization

type: number maps to java.math.BigDecimal, which has no serializer() for the generated value serializer to call, so these enums referenced an unresolved symbol. Inline number enums keep the plain @serializable they had before, while integer, long, float and double enums still use the value serializer.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][Kotlin] Inline number enums are serialized as strings with kotlinx_serialization

1 participant