Skip to content

[Bug]: Explicit @Schema(type = "number"|"integer"|"boolean") is emitted as "string" under OpenAPI 3.1 #5233

Description

@forhadmethun

Description of the problem/issue

When generating an OpenAPI 3.1 schema, an explicit @Schema(type = "...") on a property is resolved incorrectly. The scalar type is set correctly on the Schema object, but the types set is populated with ["string"]. Since the 3.1 serializer reads the types set, the property is emitted as type: "string" regardless of the declared type.

  • Goal: produce a correct OpenAPI 3.1 document for DTOs that use explicit @Schema(type = ...).
  • Annotation misbehaving: @Schema — an explicit type = "number" | "integer" | "boolean" is emitted as "string".
  • Correct case: properties relying on type inference (no explicit type in @Schema) serialize correctly. Only an explicitly declared scalar type is affected.
  • Framework: reproduces on plain swagger-core (ModelConverters + Json31); also observed via springdoc-openapi with springdoc.api-docs.version=openapi_3_1.
  • Not examples-related: it reproduces on fields with no example, so it is distinct from [Bug]: Number example treated as a string when generating swagger file. #5061 / fix: treat number example as number and not string #5062 (numeric example serialization).
  • Downstream: the resulting spec misreports field types, which breaks OpenAPI-based type/client generation (numbers/booleans generated as string).

Under OpenAPI 3.0 (openapi31 = false) the same DTO serializes correctly, so this is specific to 3.1 generation.

Affected Version

2.2.47

Earliest version the bug appears in (if known):
Not fully bisected. Confirmed present in 2.2.47 and still present in 2.2.52 (latest release at time of filing).

Steps to Reproduce

  1. Add a DTO with explicit @Schema(type = ...) properties and resolve it in OpenAPI 3.1 mode:

    import io.swagger.v3.core.converter.AnnotatedType;
    import io.swagger.v3.core.converter.ModelConverters;
    import io.swagger.v3.core.converter.ResolvedSchema;
    import io.swagger.v3.core.util.Json31;
    import io.swagger.v3.oas.annotations.media.Schema;
    import java.math.BigDecimal;
    
    public class Repro {
      enum Freq { DAY, WEEK, MONTH }
    
      static class Dto {
        @Schema(title = "Inferred") public BigDecimal inferred;         // no explicit type (control)
        @Schema(title = "Amount", type = "number")  public BigDecimal amount;
        @Schema(title = "Count",  type = "integer") public Integer count;
        @Schema(title = "Flag",   type = "boolean") public Boolean flag;
        @Schema(title = "Unit")   public Freq unit;                     // enum (control)
      }
    
      public static void main(String[] args) {
        ModelConverters converters = new ModelConverters(true); // openapi31 = true
        ResolvedSchema resolved =
            converters.resolveAsResolvedSchema(new AnnotatedType(Dto.class));
    
        // root cause: scalar type is correct, but the "types" set is ["string"]
        resolved.schema.getProperties().forEach((name, s) ->
            System.out.println(name + " -> getType()=" + s.getType() + " getTypes()=" + s.getTypes()));
    
        System.out.println(Json31.pretty(resolved.schema));
      }
    }
  2. Resolve with new ModelConverters(true) (OpenAPI 3.1 mode) and serialize with Json31.

  3. Observe the serialized output and inspect the resolved Schema objects.

Expected Behavior

The declared type is preserved:

{
  "properties": {
    "inferred": { "type": "number" },
    "amount":   { "type": "number" },
    "count":    { "type": "integer" },
    "flag":     { "type": "boolean" },
    "unit":     { "type": "string", "enum": ["DAY", "WEEK", "MONTH"] }
  }
}

Actual Behavior

Every property with an explicit scalar type collapses to "string":

{
  "properties": {
    "inferred": { "type": "number" },                                  // OK (inferred, control)
    "amount":   { "type": "string" },                                  // WRONG, declared "number"
    "count":    { "type": "string" },                                  // WRONG, declared "integer"
    "flag":     { "type": "string" },                                  // WRONG, declared "boolean"
    "unit":     { "type": "string", "enum": ["DAY", "WEEK", "MONTH"] }  // OK (enum, control)
  }
}

inferred (inferred type) and unit (enum) serialize correctly — the defect is limited to an explicitly declared scalar type.

Inspecting the resolved (pre-serialization) Schema objects shows the root cause — the scalar type is right, the types set is wrong:

amount -> getType()="number"  getTypes()=["string"]
count  -> getType()="integer" getTypes()=["string"]
flag   -> getType()="boolean" getTypes()=["string"]

Logs / Stack Traces

No exception or log output — the schema is generated silently with the wrong type.

Additional Context

Checklist

  • I have searched the existing issues and this is not a duplicate.
  • I have provided sufficient information for maintainers to reproduce the issue.

Repro.java

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