Skip to content

Kotlin parent class's field is not properly marked as nullable #3304

Description

@Jasz

Describe the bug

In Kotlin, if I have an abstract class or an interface with a nullable field, that field is not marked as nullable. Its children have that field correctly marked as nullable.

To Reproduce

  • Create a Kotlin project with Spring Boot 4.1.0, springdoc-openapi-starter-webmvc-api 3.0.3, and kotlin-reflect 2.4.0
  • Create an abstract class or an interface with a nullable field
  • Create at least one implementation of it
  • Expose it in an endpoint
plugins {
    id("org.springframework.boot") version "4.1.0"
    kotlin("jvm") version "2.4.0"
    kotlin("plugin.spring") version "2.4.0"
}

dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:4.1.0"))
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springdoc:springdoc-openapi-starter-webmvc-api:3.0.3")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
}
@RestController
class HelloController {
    @GetMapping("/hello")
    fun hello(): Shape = Circle(label = null)
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(
    JsonSubTypes.Type(value = Circle::class, name = "circle"),
    JsonSubTypes.Type(value = Square::class, name = "square"),
)
abstract class Shape {
    abstract val label: String?
}

data class Circle(override val label: String?) : Shape()

data class Square(override val label: String?) : Shape()

The Shape's label is "label": { "type": "string" }. Circle's is "label": { "type": ["string", "null"] }.

Expected behavior

I would expect Shape's label to be the same as Circle's: "label": { "type": ["string", "null"] }.

Additional context

Full open-api.json for the above
{
  "openapi": "3.1.0",
  "info": {
    "title": "OpenAPI definition",
    "version": "v0"
  },
  "servers": [
    {
      "url": "http://localhost:8899",
      "description": "Generated server url"
    }
  ],
  "paths": {
    "/hello": {
      "get": {
        "tags": [
          "hello-controller"
        ],
        "operationId": "hello",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Circle"
                    },
                    {
                      "$ref": "#/components/schemas/Square"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Circle": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Shape"
          },
          {
            "type": "object",
            "properties": {
              "label": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        ]
      },
      "Shape": {
        "type": "object",
        "discriminator": {
          "propertyName": "type"
        },
        "properties": {
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string"
          }
        },
        "required": [
          "type"
        ]
      },
      "Square": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Shape"
          },
          {
            "type": "object",
            "properties": {
              "label": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        ]
      }
    }
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions