Skip to content

[kotlin-server] fix: discriminator serialized twice (use As.EXISTING_PROPERTY) - #25543

Open
Inf1mum wants to merge 2 commits into
OpenAPITools:masterfrom
Inf1mum:fix/kotlin-server-discriminator-existing-property
Open

Inf1mum wants to merge 2 commits into
OpenAPITools:masterfrom
Inf1mum:fix/kotlin-server-discriminator-existing-property

Conversation

@Inf1mum

@Inf1mum Inf1mum commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #25524.

kotlin-server rendered @JsonTypeInfo(..., include = As.PROPERTY, property = "kind", visible = ...) on the parent class of a polymorphic hierarchy, while every mapped child model also declares (or inherits) the discriminator property — KotlinServerCodegen guarantees that by always adding/updating the property on each mapped child. Jackson therefore serialized the discriminator twice (As.PROPERTY injects it, plus the regular property), producing invalid JSON like {"kind":"CAT","kind":"CAT","lives":9}.

What changed

  • kotlin-server/typeInfoAnnotation.mustache: As.PROPERTY → As.EXISTING_PROPERTY, so Jackson reuses the already-declared discriminator property instead of injecting a synthetic copy (one-token change, as suggested in the issue).
  • Extended the existing oneOfWithDiscriminator unit test to assert As.EXISTING_PROPERTY is rendered (and As.PROPERTY is not).
  • Regenerated all 16 kotlin-server sample configs; the only content changes are the three polymorphism samples' Pet.kt annotation line.

Verification

  • KotlinServerCodegenTest: 30 tests, 0 failures (covers oneOf and allOf hierarchies, sealed-class parent, and the fixJacksonJsonTypeInfoInheritance=false scenario).
  • The issue reporter verified the same change locally: serialization now emits {"kind":"CAT","lives":9} instead of the duplicated key.

PR checklist

  • Read the contribution guidelines.
  • Regenerated the affected samples via ./bin/generate-samples.sh (all kotlin-server configs); no files deleted.
  • Pull request is created against the master branch.

Summary by cubic

Fixes duplicated discriminator keys in kotlin-server polymorphic models by reusing the existing property instead of injecting a copy, and makes the discriminator visible on deserialization so round-trips preserve it.

  • Switches typeInfoAnnotation.mustache to As.EXISTING_PROPERTY so Jackson serializes the discriminator once, not twice.
  • Emits visible = true unconditionally so deserialized children (including the fixJacksonJsonTypeInfoInheritance=false path) keep their discriminator property populated.
  • Updates the oneOfWithDiscriminator tests to assert the new include type and visible = true.
  • Regenerates kotlin-server samples; only three polymorphism-related Pet.kt files change.

Written for commit fde104a. Summary will update on new commits.

View guided diff

…info

Every mapped child model declares or inherits the discriminator
property (KotlinServerCodegen guarantees that), so As.PROPERTY made
Jackson serialize the discriminator twice - once injected, once as
the regular property - producing invalid JSON with a duplicated key.

Fixes OpenAPITools#25524

@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.

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/server/others/kotlin-server/polymorphism-allof-and-discriminator/src/main/kotlin/org/openapitools/server/models/Pet.kt">

<violation number="1" location="samples/server/others/kotlin-server/polymorphism-allof-and-discriminator/src/main/kotlin/org/openapitools/server/models/Pet.kt:20">
P2: `As.EXISTING_PROPERTY` now makes serialization depend on the caller-supplied `petType`, but these allOf child models accept any string rather than defaulting or constraining it to `Cat`/`Dog`. A `Cat` with another value emits an unmappable discriminator and cannot round-trip through `Pet`; generate subtype-specific defaults/constants for these children or otherwise ensure the existing property always contains the mapped subtype value before using this mode.</violation>
</file>

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

View guided diff | Re-trigger cubic

* @param petType
*/
@com.fasterxml.jackson.annotation.JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY, property = "petType", visible = true)
@com.fasterxml.jackson.annotation.JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.EXISTING_PROPERTY, property = "petType", visible = true)

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.

P2: As.EXISTING_PROPERTY now makes serialization depend on the caller-supplied petType, but these allOf child models accept any string rather than defaulting or constraining it to Cat/Dog. A Cat with another value emits an unmappable discriminator and cannot round-trip through Pet; generate subtype-specific defaults/constants for these children or otherwise ensure the existing property always contains the mapped subtype value before using this mode.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/server/others/kotlin-server/polymorphism-allof-and-discriminator/src/main/kotlin/org/openapitools/server/models/Pet.kt, line 20:

<comment>`As.EXISTING_PROPERTY` now makes serialization depend on the caller-supplied `petType`, but these allOf child models accept any string rather than defaulting or constraining it to `Cat`/`Dog`. A `Cat` with another value emits an unmappable discriminator and cannot round-trip through `Pet`; generate subtype-specific defaults/constants for these children or otherwise ensure the existing property always contains the mapped subtype value before using this mode.</comment>

<file context>
@@ -17,7 +17,7 @@ package org.openapitools.server.models
  * @param petType 
  */
-@com.fasterxml.jackson.annotation.JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY, property = "petType", visible = true)
+@com.fasterxml.jackson.annotation.JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.EXISTING_PROPERTY, property = "petType", visible = true)
 @com.fasterxml.jackson.annotation.JsonSubTypes(
     com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = Cat::class, name = "Cat"),
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same fix as above thread — see the reply there (fde104a makes the property authoritative via visible=true).

With As.EXISTING_PROPERTY the discriminator is read from and written
to the real property that every mapped child model declares or
inherits. Emit visible=true unconditionally so the property is also
populated on deserialization - otherwise a deserialized child (in the
fixJacksonJsonTypeInfoInheritance=false path) would serialize without
its discriminator. This is the canonical Jackson pattern for a
discriminator modeled as a real property.

Addresses review feedback in OpenAPITools#25543.

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-SERVER] typeInfoAnnotation.mustache uses As.PROPERTY → discriminator property serialized twice

1 participant