swagger-api / swagger-api/swagger-core
[Bug]: Explicit @Schema(type = "number"|"integer"|"boolean") is emitted as "string" under OpenAPI 3.1
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 7.5k
- Forks
- 2.3k
- Avg merge
- 18h 1m
- Merged PRs (30d)
- 10
Description
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 explicittype = "number" | "integer" | "boolean"is emitted as"string". - Correct case: properties relying on type inference (no explicit
typein@Schema) serialize correctly. Only an explicitly declared scalartypeis affected. - Framework: reproduces on plain swagger-core (
ModelConverters+Json31); also observed via springdoc-openapi withspringdoc.api-docs.version=openapi_3_1. - Not examples-related: it reproduces on fields with no
example, so it is distinct from #5061 / #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
-
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)); } } -
Resolve with
new ModelConverters(true)(OpenAPI 3.1 mode) and serialize withJson31. -
Observe the serialized output and inspect the resolved
Schemaobjects.
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
- The same DTO serialized under OpenAPI 3.0 (
new ModelConverters(false)) produces correct types, confirming this is a 3.1-generation regression. - Likely related to #4679 (
@Schematype/implementation ignored under OpenAPI 3.1). - Distinct from #5061 / #5062, which concern numeric
exampleserialization; this issue is about the propertytypeand reproduces with noexamplepresent. - Current workaround: a post-processing pass that re-syncs each schema's
typesset to its scalartypeproduces a correct 3.1 document.
Checklist
- I have searched the existing issues and this is not a duplicate.
- I have provided sufficient information for maintainers to reproduce the issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the attached Repro.java, then trace ModelConverters(true) into the resolved Schema objects and Json31 serialization. Compare getType() and getTypes() for explicit scalar @Schema declarations against inferred and enum controls. Done means OpenAPI 3.1 output preserves number, integer, and boolean, while OpenAPI 3.0 behavior remains correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi
- Domain
- api
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100