Repository navigation
Swagger 3.X Migration from 2.X
NOTE: This page describes how to move from Swagger Core 2.X to Swagger Core 3.0. Still on 2.X? The rest of this wiki (Getting started, Integration and Configuration, Annotations, Extending swagger) describes 2.X; the behaviour it explains is the same in 3.0, only the names, versions and Jackson APIs listed below change.
Swagger Core 3.0 is a major release:
- Java 17+ is required.
- The runtime moves from Jackson 2 to Jackson 3 (
tools.jackson). Swagger Core 3.0 is not compatible with Jackson 2. - Only the
jakarta.*namespace is supported (Jakarta EE 11 minimum). The paralleljavax/-jakartaartifact sets are gone. - The Jakarta REST module is renamed:
swagger-jaxrs2becomesswagger-rest(packagesio.swagger.v3.jaxrs2becomeio.swagger.v3.rest). - OpenAPI support is unchanged: OAS 3.0 and 3.1.
| Swagger Core 2.X | Swagger Core 3.0 | |
|---|---|---|
| Java | 8+ | 17+ |
| OpenAPI | 3.0 and 3.1 | 3.0 and 3.1 |
| Namespace |
javax.* and jakarta.* (-jakarta artifacts) |
jakarta.* only |
| Jackson | 2.x (com.fasterxml.jackson) |
3.x (tools.jackson) |
| Jakarta REST API |
jakarta.ws.rs-api 2.x (javax.ws.rs) |
jakarta.ws.rs-api 4.0.0 (jakarta.ws.rs) |
| Jakarta Servlet API |
jakarta.servlet-api 4.x (javax.servlet) |
jakarta.servlet-api 6.1.0 (jakarta.servlet) |
- Upgrade to the latest 2.X release first, and get your build and tests green there. This separates problems caused by Swagger Core from problems caused by Java, Jakarta or Jackson upgrades.
- Make sure your runtime meets the requirements above: Java 17+ and a Jakarta EE 11 compatible container or libraries.
- Check the libraries that depend on Swagger Core (frameworks, plugins, generators). Anything compiled against 2.X needs a 3.0-compatible release or a rebuild.
- Jakarta and Jackson have their own migration guides; this page covers only what changes in Swagger Core. See the Jackson 3 release notes.
For a Jakarta REST application, the dependencies in Getting started become:
<dependencies>
...
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-rest</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-rest-servlet-initializer</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>or in Gradle (Kotlin DSL):
implementation("io.swagger.core.v3:swagger-rest:3.0.0")
implementation("io.swagger.core.v3:swagger-rest-servlet-initializer:3.0.0")Everything else in the 2.X pages (annotations, configuration properties, scanning, Reader, filters, ...) works as documented there, apart from the renames in this page.
Eight library artifact IDs are retired and have no 3.0 release; replace them with the new IDs (group io.swagger.core.v3, version 3.0.0):
| 2.X artifact ID | 3.0 artifact ID |
|---|---|
swagger-annotations-jakarta |
swagger-annotations |
swagger-models-jakarta |
swagger-models |
swagger-core-jakarta |
swagger-core |
swagger-integration-jakarta |
swagger-integration |
swagger-jaxrs2 |
swagger-rest |
swagger-jaxrs2-jakarta |
swagger-rest |
swagger-jaxrs2-servlet-initializer-v2 |
swagger-rest-servlet-initializer |
swagger-jaxrs2-servlet-initializer-v2-jakarta |
swagger-rest-servlet-initializer |
The Maven plugin swagger-maven-plugin-jakarta is retired as well; use swagger-maven-plugin.
Give each dependency version 3.0.0, or import the 3.0 BOM and omit the version. The BOM does not manage retired IDs, so a versionless retired ID will not resolve.
Libraries compiled against Swagger Core 2.X need a 3.0-compatible release (or a rebuild); a BOM or an exclusion cannot update their compiled code.
Jackson core, databind and dataformat artifacts moved from com.fasterxml.jackson to tools.jackson. Jackson annotations did not move; they stay on com.fasterxml.jackson.core at version 2.22.
| Artifact | 2.X groupId | 3.0 groupId | 3.0 version |
|---|---|---|---|
jackson-core |
com.fasterxml.jackson.core |
tools.jackson.core |
3.2.3 |
jackson-databind |
com.fasterxml.jackson.core |
tools.jackson.core |
3.2.3 |
jackson-dataformat-yaml |
com.fasterxml.jackson.dataformat |
tools.jackson.dataformat |
3.2.3 |
jackson-annotations |
com.fasterxml.jackson.core |
com.fasterxml.jackson.core (unchanged) |
2.22 |
In code, replace the imports com.fasterxml.jackson.core, com.fasterxml.jackson.databind and com.fasterxml.jackson.dataformat with their tools.jackson equivalents. Keep com.fasterxml.jackson.annotation imports as they are.
org.yaml:snakeyaml is no longer used. YAML is provided by org.snakeyaml:snakeyaml-engine, which comes in transitively with jackson-dataformat-yaml; remove any explicit SnakeYAML dependency.
3.0 needs jakarta.ws.rs-api 4.0.0 and jakarta.servlet-api 6.1.0. Older versions of these APIs (including the javax.* ones used by 2.X) are not compatible and cause compile errors or ClassNotFoundException at runtime.
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
</dependency>Swagger Core 3.0 is built against jakarta.validation-api 3.1.1. If you use Bean Validation, keep jakarta.validation-api compatible with your validation provider.
| 2.X package | 3.0 package |
|---|---|
io.swagger.v3.jaxrs2 |
io.swagger.v3.rest |
io.swagger.v3.jaxrs2.ext |
io.swagger.v3.rest.ext |
io.swagger.v3.jaxrs2.util |
io.swagger.v3.rest.util |
io.swagger.v3.jaxrs2.integration |
io.swagger.v3.rest.integration |
io.swagger.v3.jaxrs2.integration.api |
io.swagger.v3.rest.integration.api |
io.swagger.v3.jaxrs2.integration.resources |
io.swagger.v3.rest.integration.resources |
io.swagger.v3.jaxrs2.integration.servlet |
io.swagger.v3.rest.integration.servlet |
| 2.X type | 3.0 type |
|---|---|
JaxrsOpenApiContextBuilder |
RestOpenApiContextBuilder |
JaxrsOpenApiContext |
RestOpenApiContext |
JaxrsAnnotationScanner |
RestAnnotationScanner |
JaxrsApplicationScanner |
RestApplicationScanner |
JaxrsApplicationAndAnnotationScanner |
RestApplicationAndAnnotationScanner |
JaxrsApplicationAndResourcePackagesAnnotationScanner |
RestApplicationAndResourcePackagesAnnotationScanner |
JaxrsOpenApiScanner |
RestOpenApiScanner |
Everywhere the 2.X pages mention io.swagger.v3.jaxrs2... or a Jaxrs* type, use the 3.0 name instead. In practice:
-
Java imports and reflection - new packages and type names.
-
Scanner configuration -
openApi.configuration.scannerClassandscannerClassinopenapi.yaml/jsontake the new class names. -
Servlet and Jersey / RESTEasy registration - for example in
web.xml:<servlet> <servlet-name>openapi</servlet-name> <servlet-class>io.swagger.v3.rest.integration.OpenApiServlet</servlet-class> <init-param> <param-name>openApi.configuration.resourcePackages</param-name> <param-value>com.example.resources</param-value> </init-param> </servlet>
and
io.swagger.v3.rest.integration.resourcesinjersey.config.server.provider.packages(or...resources.OpenApiResourceinjersey.config.server.provider.classnames). -
Custom
OpenAPIExtension- importio.swagger.v3.rest.ext.OpenAPIExtension, and rename the service fileMETA-INF/services/io.swagger.v3.jaxrs2.ext.OpenAPIExtensiontoMETA-INF/services/io.swagger.v3.rest.ext.OpenAPIExtension(the class name inside must match your implementation's package). -
module-info.java-requires io.swagger.v3.jaxrs2becomesrequires io.swagger.v3.rest, andio.swagger.v3.jaxrs2.integration.servletbecomesio.swagger.v3.rest.integration.servlet.
These only matter if you customize Jackson. If you just use annotations and the default configuration, skip to Mapper defaults.
Jackson 3 ObjectMapper is immutable, so the processor methods of the ObjectMapperProcessor (objectMapperProcessorClass) now return the mapper instead of configuring it in place.
2.X:
import com.fasterxml.jackson.databind.ObjectMapper;
public class MyProcessor implements ObjectMapperProcessor {
@Override
public void processJsonObjectMapper(ObjectMapper mapper) {
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
}
}3.0:
import tools.jackson.databind.ObjectMapper;
public class MyProcessor implements ObjectMapperProcessor {
@Override
public ObjectMapper processJsonObjectMapper(ObjectMapper mapper) {
return mapper.rebuild()
.enable(SerializationFeature.INDENT_OUTPUT)
.build();
}
}The same applies to the other processor methods (YAML and the processOutput* variants).
If you extend Jackson classes directly (for example in a custom model resolver), renaming is not enough, signatures changed too.
| Jackson 2 | Jackson 3 |
|---|---|
JsonSerializer<T> |
ValueSerializer<T> |
JsonDeserializer<T> |
ValueDeserializer<T> |
BeanSerializerModifier |
ValueSerializerModifier |
BeanDeserializerModifier |
ValueDeserializerModifier |
SerializerProvider (parameter of serialize) |
SerializationContext |
throws IOException |
throws JacksonException |
DeserializationContext keeps its name but lives in tools.jackson.databind.
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.ValueSerializer;
public class MySerializer extends ValueSerializer<MyType> {
@Override
public void serialize(MyType value, JsonGenerator gen, SerializationContext ctxt)
throws JacksonException {
// ...
}
}For all other Jackson changes see the Jackson 3 release notes.
ObjectMapperFactory.create(TokenStreamFactory, boolean) throws IllegalArgumentException for anything other than a JsonFactory or YAMLFactory. Use ObjectMapperFactory.createJson() / createYaml().
Mappers are now immutable. Customize them through ObjectMapperFactory:
import tools.jackson.databind.cfg.DateTimeFeature;
ObjectMapperFactory.addCustomizer((builder, target) ->
builder.enable(DateTimeFeature.WRITE_DURATIONS_AS_TIMESTAMPS));Swagger Core rebuilds its shared mappers (Json, Yaml, Json31, Yaml31) when customizers change. Code that keeps its own reference to a mapper does not see later customizers:
// stale: captured once, misses customizers added later
private final ObjectMapper mapper = Json.mapper();
// fine: fetch at the point of use
void process() {
ObjectMapper mapper = Json.mapper();
// ...
}This applies to Json.mapper(), Yaml.mapper(), Json31.mapper(), Yaml31.mapper() and Json31.converterMapper(). Best practice: register all customizers once at startup, before any mapper is used.
These changes come from Jackson 3, not from Swagger Core. Jackson 3 changed the default values of many mapper features and the behaviour of some built-in types (for example java.time.Month) and of property naming. Swagger Core 3.0 does not override them by default: ObjectMapperFactory starts from Jackson 3 defaults, and Jackson 2 behaviour is opt-in. As a result, the JSON/YAML your application reads and writes can differ from 2.X even if you changed nothing in your code.
The defaults that differ from Jackson 2:
| Option | 3.0 default | With Jackson 2 compatibility |
|---|---|---|
FAIL_ON_NULL_FOR_PRIMITIVES |
true |
false |
FAIL_ON_TRAILING_TOKENS (public mappers) |
true |
false |
DETECT_PARAMETER_NAMES |
true |
false |
FIX_FIELD_NAME_UPPER_CASE_PREFIX |
true |
false |
STRIP_TRAILING_BIGDECIMAL_ZEROES |
false |
true |
WRITE_UTC_AS_OFFSET |
false |
true |
ALLOW_FINAL_FIELDS_AS_MUTATORS |
false |
true |
USE_GETTERS_AS_SETTERS |
false |
true |
ONE_BASED_MONTHS |
true |
false |
WRITE_DURATIONS_AS_TIMESTAMPS |
false |
true |
Regardless of the switch, Swagger Core always sets: FAIL_ON_UNKNOWN_PROPERTIES=false, FAIL_ON_EMPTY_BEANS=false, WRITE_DATES_AS_TIMESTAMPS=false, WRITE_ENUMS_USING_TO_STRING=true, READ_ENUMS_USING_TO_STRING=false, SORT_PROPERTIES_ALPHABETICALLY=false, NON_NULL inclusion, and WRITE_BIGDECIMAL_AS_PLAIN=true (public mappers).
To get the 2.X behaviour back, call this once at startup, before the first mapper is used:
ObjectMapperFactory.setJackson2Compatibility(true);ObjectMapperFactory.isJackson2Compatibility() reads the value; clearCustomizers() does not reset it. Options are applied in this order, later steps winning: Jackson 3 defaults, Jackson 2 compatibility, Swagger Core options, your customizers. A mapper you create yourself outside ObjectMapperFactory gets none of this.
java.time.Month is written as a number by the 3.0 mapper, even though the generated schema still describes it as a string:
| Case | Jackson 2 | 3.0 default | With Jackson 2 compatibility |
|---|---|---|---|
Month.JANUARY written |
"JANUARY" |
1 |
0 |
Numeric Month read |
0 is January |
1 is January, 0 fails |
0 is January |
Text Month read |
"JANUARY" |
"JANUARY" |
"JANUARY" |
Property names derived from acronym accessors also changed, and the switch does not restore the Jackson 2 rule. This affects property names in JSON, YAML and generated schemas:
| Accessor | Jackson 2 property | 3.0 property |
|---|---|---|
getURL() / isURL()
|
url |
URL |
getIPhone() |
iphone |
IPhone |
- Use Java 17+ for runtime and build.
- Replace retired Swagger artifact IDs, set version
3.0.0(or use the BOM). - Move Jackson dependencies to
tools.jackson.*, keepjackson-annotations2.22, dropsnakeyaml. - Update
jakarta.ws.rs-apito 4.0.0 andjakarta.servlet-apito 6.1.0. - Update imports, renamed types, scanner/servlet configuration, service descriptors and
module-info.javatoio.swagger.v3.rest. - Update custom
ObjectMapperProcessor, serializer and deserializer code. - Check for cached mappers and for tests relying on Jackson 2 defaults (or enable Jackson 2 compatibility).
- Check the runtime classpath for leftover 2.X artifacts, e.g.
mvn dependency:tree -Dscope=runtime -Dincludes=io.swagger.core.v3, or./gradlew dependencies --configuration runtimeClasspath. - Run your tests and compare the generated OpenAPI definition with the expected one.