Skip to content

[BUG] [JAVA] [SPRING] Impossible to use JSpecify with SchemaMappings #23206

@felldo

Description

@felldo

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

The generated DTO looks something like this (simplified). The issue is that JSpecify's @Nullable cannot be placed before a fully qualified type name. The annotation must appear directly before the simple type name.
When the OpenAPI generator uses schema mappings, it references types by their fully qualified name instead of adding an import, which makes @Nullable placement invalid. This affects all usages of the type like fields, getters, setters, and constructors.
The fix would be to have the generator add proper imports and use simple type names instead. This would work in most cases, but breaks down if two schema-mapped types share the same simple name, then the generator would need to fall back to fully qualified names for one of them, reintroducing the problem.

public class MyDto implements Serializable {
  
  private static final long serialVersionUID = 1L;
  
  private @Nullable a.b.c.PersonCountValue men; // Doesn't work, current behavior
  
  private a.b.c.@Nullable PersonCountValue women; // Works
  
}

[ERROR] /path/a/b/c/MyDto.java type annotation @org.jspecify.annotations.Nullable is not expected here

openapi-generator version
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.20.0</version>
OpenAPI declaration file content or url

my-api-specification.yaml

components:
  schemas:
    MyDto:
      type: object
      properties:
        men:
          $ref: '#/components/schemas/PersonCountValue'
        women:
          $ref: '#/components/schemas/PersonCountValue'
    PersonCountValue:
      type: number
      format: personCountValue
      example: 1234

my-api-config.yaml

schemaMappings:
  PersonCountValue: a.b.c.PersonCountValue

and then of course you need to set the config file in the generator with something like this

<configurationFile>
  ${project.basedir}/src/main/resources/static/my-api-config.yaml
</configurationFile>

Then no class for PersonCountValue will be generated but instead an existing class managed by yourself will be used for the type.

Generation Details

Using Java 25 with Maven

<plugin>
  <groupId>org.openapitools</groupId>
  <artifactId>openapi-generator-maven-plugin</artifactId>
  <version>${openapi-generator-maven-plugin.version}</version>
  <configuration>
    <generatorName>spring</generatorName>
    <library>spring-boot</library>
    <generateSupportingFiles>false</generateSupportingFiles>
    <generateModelTests>false</generateModelTests>
    <importMappings>
      <importMapping>Nullable=org.jspecify.annotations.Nullable</importMapping>
    </importMappings>
    <generateApiTests>false</generateApiTests>
    <configOptions>
      <interfaceOnly>true</interfaceOnly>
      <oas3>true</oas3>
      <openApiNullable>false</openApiNullable>
      <serializableModel>true</serializableModel>
      <skipDefaultInterface>true</skipDefaultInterface>
      <useResponseEntity>true</useResponseEntity>
      <useSpringBoot4>true</useSpringBoot4>
      <useJackson3>true</useJackson3>
      <useSpringController>true</useSpringController>
      <useTags>true</useTags>
      <useBeanValidation>false</useBeanValidation>
    </configOptions>
  </configuration>
</plugin>
Steps to reproduce

Generate a Dto with a SchemaMapping type, which then will be written as package.name.Type instead of importing.

Suggest a fix

There needs to be a check, that when the type is not imported, that the annotation has to be right before the type and after the package name, like its shown in my example generated Dto above

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