Skip to content

Swagger 2.X OpenAPI 3.1

Francesco Tumanischvili edited this page Sep 20, 2024 · 5 revisions

NOTE: Swagger Core 2.X produces OpenApi 3.x definition files. For more information, check out the OpenAPI specification repository. If you're looking for swagger 1.5.X and OpenApi 2.0, please refer to 1.5.X JAX-RS Setup


OpenAPI 3.1 support

Swagger Core 2.2.0 release has introduced support for OpenAPI 3.1 / JSON Schema 2020/12 in terms of representation, (de)serialization and support of annotations and code-first spec resolution (since 2.2.14).

Swagger Parser 2.1.0 release has introduced support for OpenAPI 3.1 / JSON Schema 2020/12 in terms of parsing, validation and dereferencing/resolving.

We have opted to provide such support within the 2.x line of Swagger Core and Parser, to allow using the same artifacts and APIs used for OAS 3.0, with the differences and caveats detailed below

As of September 2024, support for code-first OAS 3.1 specification resolution and annotations is provided "natively" typically by setting openapi31 property of configuration to true. Conversion from generated 3.0 to 3.1 is still available (by using convertToOpenAPI31=true) but deprecated and way less complete than "native" option.

Foreword

Although version 3.1 has been chosen for the latest OpenAPI specification, with a minor version update from 3.0, this choice has been preceded by some debate, as 3.1 is not strictly adhering to SemVer being not fully backward compatible.

We have decided to implement OAS 3.1 support in the same code line currently providing OAS 3.0 support, i.e. master branches for Swagger Core and Parser, producing the following artifacts:

  • io.swagger.core.v3:swagger-core:2.2.x
  • io.swagger.parser.v3:swagger-parser:2.1.x

The alternative would have been implementing a separated code base with own set of artifacts, e.g. io.swagger.core.v31:swagger-core. There are PROs and CONs for both alternatives, the main reasons we opted for an update of existing artifacts are compatibility and maintenance effort. This comes with a price though: code branches and updates are introduced to handle both versions, introducing complexity and a less clean code. We considered however that this is a price worth to pay given the other advantages of this approach.

A separate codebase comes with a high price in terms of maintenance effort, implementing both versions support within the same code line solves this issue.

Even if semantic versioning has been dropped, 3.1 is still a minor version update, where the majority of 3.0 constructs are compatible with 3.1. Users will be facilitated by being able to keep using the same code to process both versions.

OpenAPI 3.1 support in Swagger Core

Representation - Models

swagger-models module provides a POJO representation of a OpenAPI 3.x definition.

The full set of constructs defined in OAS 3.0, OAS 3.1 and JSON Schema Draft 2020/12 are supported; this means that all fields have a corresponding JAVA class / member.

Fields specific to OpenAPI 3.1 are annotated with @OpenAPI31, while fields only part of 3.0 version (such as Schema.nullable) are annotated with @OpenAPI3.0.

In scenarios where the data type of an existing field has changed, e.g. for Schema.exclusiveMinimum, a new member with the 3.1 data type has been added to the class alongside the existing 3.0 version. During (de)serialization (see below) the correct field will be processed depending on the spec version.

Schema type

A particular case of this scenario is the representation of Schema.type. In Swagger Core 2.2.x, when processing OpenAPI 3.1 specifications, type field is mapped by Set<String> types member (instead of String type) to also support array data type.

This means that foo schema in the following spec:

openapi: 3.1.0
...
components:
  schemas:
    foo:
      type: 
        - string
        - integer
...

is deserialized into a Schema class with populated types member (while type remains null).

The same behavior is also applied when type is not an array:

openapi: 3.1.0
...
components:
  schemas:
    foo:
      type: string
...

is deserialized into a Schema class with populated types member with a single item array (while type remains null).

In order to allow for no change support of 3.1 processing by existing clients in specific limited scenarios, users can set system property bind-type=true to have Schema.getType() return the value of the single item of Schema.types in OAS 3.1 specifications with a non-array Schema.type. This is however NOT RECOMMENDED (see this comment).

See also Foreword above, this choice has been implemented to allow to maintain the same APIs, even if it introduces some "less clean" code.

Please note that the above is not strictly related to OAS 3.1 / JSON Schema 2020/12, as type supports an array data type also in previous version, but this wasn't supported in Swagger Core versions < 2.2.x.

  • openapi
  • info
  • ✅ (3.1) jsonSchemaDialect
  • servers
  • paths
  • ✅ (3.1) webhooks
  • components
  • security
  • tags
  • externalDocs
  • extensions
  • title
  • ✅ (3.1) summary
  • description
  • termsOfService
  • contact
  • license
  • version
  • extensions
  • name
  • url
  • email
  • extensions
  • name
  • url
  • ✅ (3.1) identifier
  • extensions
  • url
  • description
  • variables
  • extensions
  • enum
  • default
  • description
  • extensions
  • schemas
  • responses
  • parameters
  • examples
  • requestBodies
  • headers
  • securitySchemes
  • links
  • callbacks
  • ✅ (3.1) pathItems
  • extensions
  • $ref
  • summary
  • description
  • get
  • put
  • post
  • delete
  • options
  • head
  • patch
  • trace
  • servers
  • parameters
  • extensions
  • tags
  • summary
  • description
  • externalDocs
  • operationId
  • parameters
  • requestBody
  • responses
  • callbacks
  • deprecated
  • security
  • servers
  • extensions
  • url
  • description
  • extensions

see also Parameter subclasses

  • $ref
  • name
  • in
  • description
  • required
  • deprecated
  • allowEmptyValue
  • style
  • explode
  • allowReserved
  • schema
  • example
  • examples
  • content
  • extensions
  • matrix
  • label
  • form
  • simple
  • spaceDelimited
  • pipeDelimited
  • deepObject
  • $ref
  • description
  • content
  • required
  • extensions
  • schema
  • example
  • examples
  • encoding
  • extensions
  • contentType
  • headers
  • style (form, spaceDelimited, pipeDelimited, deepObject)
  • explode
  • allowReserved
  • extensions
  • default
  • extensions
  • $ref
  • description
  • content
  • headers
  • links
  • extensions

Callback Object is a container for Path Item Object

  • $ref
  • description
  • summary
  • value
  • externalValue
  • extensions
  • $ref
  • operationRef
  • operationId
  • parameters
  • requestBody
  • description
  • headers
  • server
  • extensions
  • $ref
  • description
  • required
  • deprecated
  • style
  • explode
  • schema
  • example
  • examples
  • content
  • extensions
  • name
  • description
  • externalDocs
  • extensions

Reference Objects are represented by their target element, e.g. a Reference Object targeting a Parameter Object is represented by the Parameter class.

Schema object is the construct which has most differences between the OAS 3.0 / JSON Schema Draft 04 and the OAS 3.1 / JSON Schema Draft 2020/12 versions. New keywords have been introduced, some keywords have been updated in terms of data type and dereferencing / resolving rules have also changed.

In order to support these changes with the minimal possible impact on existing APIs, Schema related classes have been refactored (maintaining backward compatibility), basically applying the following:

  1. "Specific Schema classes" members (like StringSchema, ArraySchema, etc) have been moved to base class Schema.java
  2. OAS 3.1 specific fields have been added to Schema.java
  3. In scenarios where the data type of an existing field has changed, e.g. for Schema.exclusiveMinimum, a new member with the 3.1 data type has been added to the class alongside the existing 3.0 version. During (de)serialization (see below) the correct field will be processed depending on the spec version.
  4. Deserialization for OAS 3.0 schemas still results in a POJO structure with specific sub classes (e.g. a schema of type string is deserialized into a StringSchema) while deserialization for OAS 3.1 schemas results in a POJO structure where only base Schema class is involved.
  5. See section above about Schema type vs types
  6. JSON Schema boolean value is represented as a Schema class with field booleanSchemaValue populated (with either true or false).

Fields below represent keywords supported by Draft 4 and/or Draft 2020-12.

  • $ref
  • default
  • name
  • title
  • multipleOf
  • maximum
  • ✅ (3.0) exclusiveMaximum (Boolean exclusiveMaximum)
  • ✅ (3.1) exclusiveMaximum (BigDecimal exclusiveMaximumValue)
  • minimum
  • ✅ (3.0) exclusiveMinimum (Boolean exclusiveMinimum)
  • ✅ (3.1) exclusiveMaximum (BigDecimal exclusiveMinimumValue)
  • maxLength
  • minLength
  • uniqueItems
  • maxProperties
  • minProperties
  • required
  • ✅ (3.0) type (String type)
  • ✅ (3.1) type (Set<String> types)
  • not
  • properties
  • additionalProperties
  • description
  • extensions
  • description
  • format
  • ✅ (3.0) nullable
  • readOnly
  • writeOnly
  • example
  • externalDocs
  • deprecated
  • xml
  • extensions
  • enum
  • discriminator
  • ✅ (3.1) prefixItems
  • allOf
  • anyOf
  • oneOf
  • items
  • maxItems
  • minItems
  • pattern
  • const
  • ✅ (3.1) patternProperties
  • ✅ (3.1) contains
  • ✅ (3.1) $id
  • ✅ (3.1) $schema
  • ✅ (3.1) $anchor
  • ✅ (3.1) contentEncoding
  • ✅ (3.1) contentMediaType
  • ✅ (3.1) contentSchema
  • ✅ (3.1) propertyNames
  • ✅ (3.1) unevaluatedProperties
  • ✅ (3.1) maxContains
  • ✅ (3.1) minContains
  • ✅ (3.1) additionalItems
  • ✅ (3.1) unevaluatedItems
  • ✅ (3.1) if
  • ✅ (3.1) then
  • ✅ (3.1) else
  • ✅ (3.1) dependentSchemas
  • ✅ (3.1) dependentRequired
  • ✅ (3.1) $comment
  • ✅ (3.1) examples
  • propertyName
  • mapping
  • extensions
  • name
  • namespace
  • prefix
  • attribute
  • wrapped
  • extensions
  • $ref
  • type (apiKey, http, oauth2, openIdConnect, mutualTLS)
  • description
  • name
  • in (cookie, header, query)
  • scheme
  • bearerFormat
  • flows
  • openIdConnectUrl
  • extensions
  • implicit
  • password
  • clientCredentials
  • authorizationCode
  • extensions
  • authorizationUrl
  • tokenUrl
  • refreshUrl
  • scopes
  • extensions

Extensions are represented by the extensions field of the specific class representing the property holding the extension. When deserializing OAS 3.1 specs, an additional constraint is applied NOT deserializing extensions with x-oas or x-oai prefix which is reserved.

Serialization and Deserialization - Core

In accordance to what detailed in Foreword, (de)serialization logic depends on the spec version: Swagger Core provides separate helper classes and configured ObjectMapper instances for 3.0 and 3.1, with both deserializing into the same OpenAPI POJO and serializing from it into a valid 3.0 or 3.1 document.

Up to versions < 2.2.x the available helper classes wrapping customized ObjectMapper were:

These classes are unchanged and still the ones to use for 3.0 specifications for deserialization (Json.mapper().readValue(specAsJsonString, OpenAPI.class)) and serialization (Yaml.pretty(openAPI)).

Versions >= 2.2.x introduce the corresponding 3.1 helper classes:

Their usage is analogous both for deserialization (Yaml31.mapper().readValue(specAsYamlString, OpenAPI.class)) and serialization (Json31.pretty(openAPI)).

Please note that serializing an OpenAPI instance with one version (say Yaml.pretty()) deserialized with a different version (say Yaml31.mapper().readValue(specAsYamlString, OpenAPI.class)) will possibly result in an incorrect specifications for any fields which are different among the versions.

Resolution - Core, JAX-RS, Annotations

As of September 2024, support for code-first OAS 3.1 specification is available via "native" processing of OAS 3.1 constructs.

Since version 2.2.14 a boolean configuration property openAPI31 is available: if set to true the resolved spec will be created as OAS 3.1 by resolving according to OAS 3.1 rules (e.g. $refs siblings) and using specific 3.1 annotations when available.

OpenAPI 3.1 support in Swagger Parser

Parsing

Swagger Parser parses an OpenAPI specification into an OpenAPI instance provided by swagger-models module of Swagger Core. Since version 2.1.0 it also supports parsing and validating OpenAPI 3.1 specifications with the same APIs used for OAS 3.0. It detects the version by checking the value of openapi field:

SwaggerParseResult result = new OpenAPIParser().readLocation("./path/to/openapi31.yaml", null, null);
OpenAPI openapi = result.getOpenAPI();

The returned OpenAPI instance will be analogous to the one obtained by Swagger Core deserialization, as detailed above.

Validation

Since version 2.1.0 validation is also supporting OAS 3.1 specification rules, which are applied when version 3.1 is detected by checking the value of openapi field. Any validation errors are provided as for 3.0 within SwaggerParseResult.

Resolving, Dereferencing, Bundling

When processing OpenAPI 3.1 specification, Swagger Parser uses a different logic to perform resolving (options.setResolve(true)) than the one used for 3.0 documents. Such logic is meant to be more consistent and maintainable and as of November 2022 its outcome is still different from the one for 3.0, with ongoing effort to have it provide the same behavior of 3.0 with appropriate options.

The goal is also to apply the same mechanism for 3.0 specification, adapted to comply with OAS 3.0 dereference rules.

The main difference is that when invoked with options.setResolve(true) (without resolveFully) for 3.1 specs, external references are replaced with their content inline (similar to resolveFully) instead of being added to the root document components and with the original $ref updated to point to the local target as it's the case for 3.0. Internal references behavior for parameters and responses changes from 3.0 to 3.1. If the 3.0 behavior is needed, resolveFully option must be set to true.

Clone this wiki locally