[BUG][KOTLIN] kotlinx_serialization: a string-or-number anyOf/oneOf with number mapped to kotlin.Long/kotlin.Double still doesn't compile in 7.27.0

Aperta
#25,587 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@cpruijsen ci sta già lavorando.

Dal 12/11/2026.

  • #25645 di @cpruijsen — aperta

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
76/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
java, kotlin
Ambito
backend, tooling

Direzione di ricerca

Read kotlin-client/anyof_class.mustache and oneof_class.mustache, focusing on the serialize() branches for primitive numeric types and how type mappings affect their flags. Reproduce the failure with the Kotlin generator and kotlinx_serialization options in the issue, then run the generated client’s compileKotlin task. Done when generated string-or-number anyOf and oneOf models compile and encode every numeric branch as a JSON number.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

[!NOTE]
🤖 Claude Opus 5.5 opening this issue on behalf of Will

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
Description

Follow-up to #25452, which #25472 closed. With the kotlin generator, serializationLibrary=kotlinx_serialization and generateOneOfAnyOfWrappers=true, a string-or-number anyOf/oneOf still doesn't compile on 7.27.0 when the number is mapped to a Kotlin type (--type-mappings=integer=kotlin.Long,number=kotlin.Double). Mapping integer to Long is common because epoch milliseconds overflow an Int.

when (value) {
    is AnyOfValue.StringValue -> jsonEncoder.encodeString(value.value)
}

when (value) {
    is AnyOfInteger.StringValue -> jsonEncoder.encodeString(value.value)
    is AnyOfInteger.LongValue -> jsonEncoder.encodeInt(value.value)
}
e: build/generated/src/main/kotlin/repro/models/AnyOfInteger.kt:67:64 Argument type mismatch: actual type is 'Long', but 'Int' was expected.
e: build/generated/src/main/kotlin/repro/models/AnyOfValue.kt:65:9 'when' expression must be exhaustive. Add the 'is DoubleValue' branch or an 'else' branch.
e: build/generated/src/main/kotlin/repro/models/OneOfInteger.kt:66:64 Argument type mismatch: actual type is 'Long', but 'Int' was expected.
e: build/generated/src/main/kotlin/repro/models/OneOfValue.kt:64:9 'when' expression must be exhaustive. Add the 'is DoubleValue' branch or an 'else' branch.

In kotlin-client/anyof_class.mustache and oneof_class.mustache, #25472 added an {{#isNumber}} case under {{^isPrimitiveType}} (the unmapped BigDecimal). A mapped kotlin.Double is a primitive, so it reaches the {{#isPrimitiveType}} cases, where a number is written only under {{#isDouble}} or {{#isFloat}}, and an unformatted number sets neither. An unformatted integer sets isInteger but not isLong, so it's written with encodeInt even when mapped to kotlin.Long.

openapi-generator version

7.27.0 (CLI from Maven Central). Not a regression: 7.26.0 fails the same way for these mappings.

OpenAPI declaration file content or url
openapi: 3.0.3
info:
  title: String or number
  version: 1.0.0
paths:
  /any:
    get:
      operationId: getAnyOf
      responses:
        "200":
          description: A string or a number
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AnyOfValue"
components:
  schemas:
    AnyOfValue:
      anyOf:
        - type: string
        - type: number
    OneOfValue:
      oneOf:
        - type: string
        - type: number
    AnyOfInteger:
      anyOf:
        - type: string
        - type: integer
    OneOfInteger:
      oneOf:
        - type: string
        - type: integer
Generation Details
java -jar openapi-generator-cli-7.27.0.jar generate -g kotlin --library jvm-retrofit2 -i openapi.yaml -o generated \
  --package-name repro \
  --additional-properties=serializationLibrary=kotlinx_serialization,useCoroutines=true,generateOneOfAnyOfWrappers=true \
  --type-mappings=integer=kotlin.Long,number=kotlin.Double
Steps to reproduce

https://github.com/ceolinwill/openapi-generator-kotlin-anyof-number-repro runs the 7.27.0 CLI with those options and compiles the client:

./gradlew compileKotlin

It fails with the four errors above. Expected: the client compiles, and every number branch is written as a JSON number.

Related issues/PRs

#25452, #25472

Suggest a fix

Write every numeric primitive branch the same way, with one case in place of the isInteger/isLong/isNumber cases under {{#isPrimitiveType}} in serialize():

{{#isNumeric}}
is {{classname}}.{{#fnToValueClassName}}{{{dataType}}}{{/fnToValueClassName}} -> jsonEncoder.encodeJsonElement(JsonPrimitive(value.value))
{{/isNumeric}}

With that, unions of string with integer, int64, number, double and float compile and round-trip a number, with and without the mappings. We override anyof_class.mustache in our project with the narrower equivalent (encodeLong for the integer case and an encodeDouble case for a number without isDouble/isFloat) until a release fixes it.

Lingua principale
Java
Stelle
26.9k
Fork
7.8k
Merge medio
2g 10h
PR unite (30g)
180

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di OpenAPITools/openapi-generator

Tutte le issue di OpenAPITools/openapi-generator

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.