Skip to content

Swagger 3.X Migration from 2.X

Daniel Kmiecik edited this page Oct 16, 2026 · 3 revisions

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.


What changes in 3.0

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 parallel javax / -jakarta artifact sets are gone.
  • The Jakarta REST module is renamed: swagger-jaxrs2 becomes swagger-rest (packages io.swagger.v3.jaxrs2 become io.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)

Before you start

  • 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.

Quick start (3.0)

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.


Artifacts

Swagger artifact IDs

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

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.

Jakarta REST and Servlet APIs

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.


Packages and types

Renamed packages

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

Renamed types

2.X type 3.0 type
JaxrsOpenApiContextBuilder RestOpenApiContextBuilder
JaxrsOpenApiContext RestOpenApiContext
JaxrsAnnotationScanner RestAnnotationScanner
JaxrsApplicationScanner RestApplicationScanner
JaxrsApplicationAndAnnotationScanner RestApplicationAndAnnotationScanner
JaxrsApplicationAndResourcePackagesAnnotationScanner RestApplicationAndResourcePackagesAnnotationScanner
JaxrsOpenApiScanner RestOpenApiScanner

What to update

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.scannerClass and scannerClass in openapi.yaml/json take 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.resources in jersey.config.server.provider.packages (or ...resources.OpenApiResource in jersey.config.server.provider.classnames).

  • Custom OpenAPIExtension - import io.swagger.v3.rest.ext.OpenAPIExtension, and rename the service file META-INF/services/io.swagger.v3.jaxrs2.ext.OpenAPIExtension to META-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.jaxrs2 becomes requires io.swagger.v3.rest, and io.swagger.v3.jaxrs2.integration.servlet becomes io.swagger.v3.rest.integration.servlet.


Jackson 3 changes that affect you

These only matter if you customize Jackson. If you just use annotations and the default configuration, skip to Mapper defaults.

ObjectMapperProcessor

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).

Custom serializers and deserializers

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

ObjectMapperFactory.create(TokenStreamFactory, boolean) throws IllegalArgumentException for anything other than a JsonFactory or YAMLFactory. Use ObjectMapperFactory.createJson() / createYaml().

Customizing mappers, and caching them

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.

Mapper defaults

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

Migration steps

  1. Use Java 17+ for runtime and build.
  2. Replace retired Swagger artifact IDs, set version 3.0.0 (or use the BOM).
  3. Move Jackson dependencies to tools.jackson.*, keep jackson-annotations 2.22, drop snakeyaml.
  4. Update jakarta.ws.rs-api to 4.0.0 and jakarta.servlet-api to 6.1.0.
  5. Update imports, renamed types, scanner/servlet configuration, service descriptors and module-info.java to io.swagger.v3.rest.
  6. Update custom ObjectMapperProcessor, serializer and deserializer code.
  7. Check for cached mappers and for tests relying on Jackson 2 defaults (or enable Jackson 2 compatibility).
  8. 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.
  9. Run your tests and compare the generated OpenAPI definition with the expected one.

Clone this wiki locally