Environment
- springdoc-openapi-starter-webmvc-ui: 3.1.0
- swagger-core-jakarta: 2.2.52
- Spring Boot: 4.0.7
- Jackson: 3.x (
tools.jackson.*), default for Spring Boot 4
- Immutables: 2.12.2
Describe the bug
After migrating a Spring Boot 3 project to Spring Boot 4 (which defaults to Jackson 3), all
@JsonSerialize/@JsonDeserialize annotations were moved from com.fasterxml.jackson.databind.annotation
to tools.jackson.databind.annotation, as required for correct runtime (de)serialization under Jackson 3.
After this change, generated OpenAPI schemas for the affected classes lose all properties,
required, and nullable entries. The schema is emitted as a bare {"type":"object"}.
Root cause
org.springdoc.core.providers.ObjectMapperProvider builds its jsonMapper/yamlMapper via
io.swagger.v3.core.util.Json.mapper() / Json31.mapper(), which are backed by a classic
Jackson 2 com.fasterxml.jackson.databind.ObjectMapper. swagger-core-jakarta:2.2.52's
ModelResolver looks up @JsonSerialize/@JsonDeserialize specifically as
com.fasterxml.jackson.databind.annotation.JsonSerialize/JsonDeserialize (confirmed by
reading ModelResolver.java: the @JsonSerialize(as=...) redirect loop and property
enumeration are entirely Jackson-2-typed).
A class annotated with the Jackson 3 (tools.jackson.databind.annotation) equivalents is
therefore invisible to ModelResolver: the @JsonSerialize(as=...) redirect to the concrete
implementation class never triggers, and the abstract type's own fluent (no get/is prefix)
accessors are not auto-detected without an explicit @JsonProperty — resulting in an empty
schema.
Note @JsonProperty itself stays in com.fasterxml.jackson.annotation under Jackson 3, which is why classes with an explicit @JsonProperty on
every accessor are unaffected. Only the @JsonSerialize/@JsonDeserialize-driven redirect to
a concrete implementation type breaks.
Reproduction
// Works: properties/required resolved correctly
@JsonSerialize(as = ImmutableTest.class) // com.fasterxml.jackson.databind.annotation
@Value.Immutable
public interface Test {
Boolean isActive();
Optional<String> id();
}
// Broken: schema resolves to bare {"type":"object"}, no properties/required at all
@JsonDeserialize(as = ImmutableTest.class) // tools.jackson.databind.annotation
@JsonSerialize(as = ImmutableTest.class) // tools.jackson.databind.annotation
@Value.Immutable
public interface Test {
String isActive();
Optional<String> id();
}
Standalone reproduction against swagger-core-jakarta:2.2.52 directly (no Spring context needed)
confirms this is not springdoc-specific glue code but the underlying ModelResolver itself:
calling ModelResolver.resolve() on both interfaces above, AvailabilityResponse resolves with
full properties, while AvailabilityRequest (identical shape, only the annotation package
differs) resolves to an empty object schema. Switching AvailabilityRequest's annotations back
to com.fasterxml.jackson.databind.annotation makes it resolve correctly.
Expected behavior
Since Spring Boot 4 / Spring Framework 7 officially support Jackson 3, and
spring.jackson.use-jackson2-defaults/com.fasterxml.jackson.databind.annotation.* classes are
being phased out in application code, springdoc's ModelResolver (or a springdoc-specific
wrapper around it) should also recognize tools.jackson.databind.annotation.JsonSerialize /
JsonDeserialize when resolving schemas, at least when the project's runtime ObjectMapper is
a Jackson 3 mapper.
Workaround
Explicitly annotate every fluent accessor with @JsonProperty
(com.fasterxml.jackson.annotation.JsonProperty, which is unaffected by the Jackson 3 package
split) instead of relying on the @JsonSerialize/@JsonDeserialize(as=...) redirect for schema
generation.
Environment
tools.jackson.*), default for Spring Boot 4Describe the bug
After migrating a Spring Boot 3 project to Spring Boot 4 (which defaults to Jackson 3), all
@JsonSerialize/@JsonDeserializeannotations were moved fromcom.fasterxml.jackson.databind.annotationto
tools.jackson.databind.annotation, as required for correct runtime (de)serialization under Jackson 3.After this change, generated OpenAPI schemas for the affected classes lose all properties,
required, andnullableentries. The schema is emitted as a bare{"type":"object"}.Root cause
org.springdoc.core.providers.ObjectMapperProviderbuilds itsjsonMapper/yamlMapperviaio.swagger.v3.core.util.Json.mapper()/Json31.mapper(), which are backed by a classicJackson 2
com.fasterxml.jackson.databind.ObjectMapper.swagger-core-jakarta:2.2.52'sModelResolverlooks up@JsonSerialize/@JsonDeserializespecifically ascom.fasterxml.jackson.databind.annotation.JsonSerialize/JsonDeserialize(confirmed byreading
ModelResolver.java: the@JsonSerialize(as=...)redirect loop and propertyenumeration are entirely Jackson-2-typed).
A class annotated with the Jackson 3 (
tools.jackson.databind.annotation) equivalents istherefore invisible to
ModelResolver: the@JsonSerialize(as=...)redirect to the concreteimplementation class never triggers, and the abstract type's own fluent (no
get/isprefix)accessors are not auto-detected without an explicit
@JsonProperty— resulting in an emptyschema.
Note
@JsonPropertyitself stays incom.fasterxml.jackson.annotationunder Jackson 3, which is why classes with an explicit@JsonPropertyonevery accessor are unaffected. Only the
@JsonSerialize/@JsonDeserialize-driven redirect toa concrete implementation type breaks.
Reproduction
Standalone reproduction against
swagger-core-jakarta:2.2.52directly (no Spring context needed)confirms this is not springdoc-specific glue code but the underlying
ModelResolveritself:calling
ModelResolver.resolve()on both interfaces above,AvailabilityResponseresolves withfull properties, while
AvailabilityRequest(identical shape, only the annotation packagediffers) resolves to an empty object schema. Switching
AvailabilityRequest's annotations backto
com.fasterxml.jackson.databind.annotationmakes it resolve correctly.Expected behavior
Since Spring Boot 4 / Spring Framework 7 officially support Jackson 3, and
spring.jackson.use-jackson2-defaults/com.fasterxml.jackson.databind.annotation.*classes arebeing phased out in application code, springdoc's
ModelResolver(or a springdoc-specificwrapper around it) should also recognize
tools.jackson.databind.annotation.JsonSerialize/JsonDeserializewhen resolving schemas, at least when the project's runtimeObjectMapperisa Jackson 3 mapper.
Workaround
Explicitly annotate every fluent accessor with
@JsonProperty(
com.fasterxml.jackson.annotation.JsonProperty, which is unaffected by the Jackson 3 packagesplit) instead of relying on the
@JsonSerialize/@JsonDeserialize(as=...) redirect for schemageneration.