Skip to content

[BUG][KOTLIN] oneOf does not generate type hierarchy correctly #22013

@jensbaitingerbosch

Description

@jensbaitingerbosch

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?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

When having an endpoint that returns R being oneOf a or b (or more), the generated classes for A and B do not implement the generated interface which is generated for R and therefore the deserialization with Jackson fails.

openapi-generator version

7.14.0 (gradle plugin)

OpenAPI declaration file content or url
---
openapi: 3.0.1
info:
  title: some
  version: 1.0.0
servers:
  - url: http://localhost:8080
    description: Local server


paths:
  "/someEndponint":
    get:
      summary: Get the specified device job of a single device
      description: |
        Obtain a specified device job of a single device

      operationId: someEndpoint
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                oneOf:
                  - "$ref": "#/components/schemas/A"
                  - "$ref": "#/components/schemas/B"
                discriminator:
                  propertyName: jobType
                  mapping:
                     A: "#/components/schemas/A"
                     B: "#/components/schemas/B"
components:
  schemas:
    A:
      type: object
      properties:
        jobType:
          type: string
        propertyA:
          type: string

    B:
      type: object
      properties:
        jobType:
          type: string
        propertyB:
          type: string
Generation Details

here is the plugin configuration:

tasks.register<GenerateTask>("openApiGenerateV2Client") {
        generatorName = "kotlin" 
        inputSpec = "$projectDir/src/main/resources/static/v2/open-api-docs.yaml"
        outputDir = "${layout.buildDirectory.get()}/generated/client/"
        configOptions =
            mapOf(
                "library" to "jvm-spring-restclient",
                "serializationLibrary" to "jackson",
                "useSpringBoot3" to "true",
            )
    }
Steps to reproduce

have a server that implement specified interface, call the client method, it produces the following Exception:

Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'A' as a subtype of `...SomeEndpoint200Response`: Class `...A` not subtype of `...SomeEndpoint200Response`
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 12]
	at com.fasterxml.jackson.databind.exc.InvalidTypeIdException.from(InvalidTypeIdException.java:43)
	at com.fasterxml.jackson.databind.DeserializationContext.invalidTypeIdException(DeserializationContext.java:2096)
	at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:198)
	at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:150)
	at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:135)
	at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:262)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)

Here the generated Code

data class A (

    @get:JsonProperty("jobType")
    val jobType: kotlin.String? = null,

    @get:JsonProperty("propertyA")
    val propertyA: kotlin.String? = null

)

data class B (

    @get:JsonProperty("jobType")
    val jobType: kotlin.String? = null,

    @get:JsonProperty("propertyB")
    val propertyB: kotlin.String? = null

) 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "jobType", visible = true)
@JsonSubTypes(
)

interface SomeEndpoint200Response {

    @get:JsonProperty("jobType")
    val jobType: kotlin.String?
    @get:JsonProperty("propertyA")
    val propertyA: kotlin.String?
    @get:JsonProperty("propertyB")
    val propertyB: kotlin.String?

}
Related issues/PRs
Suggest a fix

How the code should be generated (note, the Java generator creates a similar structure):

data class A : SomeEndpoint200Response (

    @get:JsonProperty("jobType")
    override val jobType: kotlin.String? = null,

    @get:JsonProperty("propertyA")
    val propertyA: kotlin.String? = null

)

data class B : SomeEndpoint200Response (

    @get:JsonProperty("jobType")
   override val jobType: kotlin.String? = null,

    @get:JsonProperty("propertyB")
    val propertyB: kotlin.String? = null

) 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "jobType", visible = true)
@JsonSubTypes(
)

interface SomeEndpoint200Response {

    @get:JsonProperty("jobType")
    val jobType: kotlin.String?

}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions