What’s New

April 2026

  • openapi-processor-spring/micronaut 2026.3

openapi-processor-spring: @HttpExchange based interfaces

Spring Boot provides two annotation families

  • the standard mapping annotations, i.e., @RequestMapping, @GetMapping and friends to define server side controllers

  • and the new exchange (HTTP service clients) annotations, i.e., @HttpExchange, @GetExchange and friends to define server side controllers and http clients based on the same interface.

This makes it possible to use the endpoint interfaces generated by openapi-processor-spring to call the endpoints. Spring does generate (with a few lines of configuration) the boilerplate code that is required to to call the api endpoints.

Switching between both annotation families is done in the mapping.yaml:

mapping.yaml
openapi-processor-spring: v1 (1)

options:
  package-name: io.openapiprocesser

  spring: (2)
    # default, i.e., mapping annotations
    #annotations: mapping
    # use exchange annotations
    annotations: exchange (3)

There are a few things that are important here:

1 the mapping identifier has changed. It is now expecting openapi-processor-spring instead of openapi-processor-mapping. The processor will still accept openapi-processor-mapping.

The new identifier is required to get proper editing support for the new option with the IntelliJ plugin.

2 a new Spring specific section in the options object.
3 the annotation family: mapping or exchange. With mapping being the default.

The openapi-processor-samples repository has a sample that provides an api endpoint and calls itself using the @HttpExchange based interface.

fixed a stack overflow error

combining a property like payload

openapi.yaml
MyRequest:
  type: object
  properties:
    payload:
      type: string
      format: byte

with a type mapping and enabling bean validation

mapping.yaml
openapi-processor-mapping: v17

options:
  package-name: ....
  bean-validation: jakarta

map:
  types:
    - type: string:byte => byte[]

caused a stack overflow error.

April 2026

openapi-processor-gradle 2026.2

This version will only work with gradle 8.7+

minimum Gradle version minimum Java version

8.7+

17

configuring the processor dependency

configuring the processor dependency is changing.

It is now properly using the Gradle API instead of a partial custom implementation. That way it will automatically handle every dependency format Gradle accepts in the project dependencies block.

The old way will still work, but it will go away in the future.

build.gradle.kts
// obsolete, "custom" implementation

openapiProcessor {
    // ....

    // still works
    process("spring") {
        processor("io.openapiprocessor:openapi-processor-spring:<version>")

        // ....
    }
}


// new, using Gradle APIs

openapiProcessor {
    // ....

    process("spring") {
        dependencies {
            process("io.openapiprocessor:openapi-processor-spring:<version>")
        }

       // ....
    }
}

March 2026

openapi-processor-gradle 2026.1

no need for afterEvaluate

In previous versions it was necessary to access the processor tasks from an afterEvaluate block. This is no longer necessary.

A simple configuration (apart from the processor configuration itself) to automatically process an OpenAPI YAML and build the generated sources can look like this:

sourceSets {
    create("api") {
        resources {
            srcDir(layout.projectDirectory.dir("src/api"))
        }
    }

    main {
        java {
            srcDir(tasks.named("processSpring"))
        }
    }
}

Gradle 10 compatibility

fixed a Gradle 10 compatibility warning

openapi-processor-maven 2026.1

added <processor/>

The <processor/> configuration is necessary to run a single processor on multiple inputs. See the documentation for more.

fixed <targetDir/>

automatic <targetDir/> selection did not select the correct folder when using <processor/>

updated dependencies

updated dependencies to maven 3.9

February 2026

  • openapi-processor-spring/micronaut 2026.2

accept 1xx/3xx as "success" codes

By default, openapi-processor expected at least one response with a 2xx status code. This caused an

endpoint '/<.. an endpoint ..>' has no success 2xx response.

error if the endpoint did have a 1xx or 3xx but no 2xx status code.

Both are not error codes; therefore, 1xx and 3xx are considered now as success responses.

application/x-www-form-urlencoded

openapi-processor does now properly handle application/x-www-form-urlencoded. This means it properly destructures the response body into method parameters like it does for multipart/form-data requests.

To receive the request body as a single object parameter, add a body-style mapping. body-style supports all the usual mapping levels: global, endpoint and endpoint method.

mapping.yaml
openapi-processor-mapping: v17

options:
  package-name: io.openapiprocessor.openapi

map:
  # global
  body-style: object
  #body-style: destructure # default

  paths:
    /foo:
      # endpoint
      body-style: object

      # endpoint method
#      post:
#        body-style: object
Currently, the body-style mapping is only supported on application/x-www-form-urlencoded.

January 2026

  • openapi-processor-spring/micronaut 2026.1

add (marker) interfaces to schema classes via mapping

this version adds interface mapping. This makes it possible to let the generated pojos/records implement (marker) interfaces.

mapping.yaml
openapi-processor-mapping: v16

options:
  package-name: io.openapiprocessor.openapi
  model-type: record

map:
  types:
    - type: Foo =+ java.io.Serializable

    # apply the mapping to ALL generated models/DTOs
    - type: object =+ java.io.Serializable

  parameters:
    - type: Bar =+ java.io.Serializable

  # can be used at endpoint level, makes only sense if the models/DTOs are specific to the endpoint
  paths:
    /foo:
      types:
        - type: Foo =+ java.io.Serializable

      parameters:
        - type: Foo =+ java.io.Serializable

      get:
        types:
          - type: Foo =+ java.io.Serializable

        parameters:
          - type: Foo =+ java.io.Serializable

This would generate a Foo model/DTO like this:

Foo.java
@Generated(value = "openapi-processor-spring",  version = "2026.1")
public record Foo(@JsonProperty("foo") String foo) implements Serializable {}

alternative mapping keywords

the mapping does understand keywords additionally to the mapping operators. Instead of using the operators, it is possible to use map, annotate, or implement.

keywords
some-key: {source type} => {target type}
some-key: {source type} map {target type}

some-key: {source type} @ {target type}
some-key: {source type} annotate {target type}

some-key: {source type} =+ {target type}
some-key: {source type} implement {target type}

oneOf interface

generation of the oneOf interface does no longer duplicate the interface in the implements list.

configure allowed targets for annotations

annotation mapping may place annotations on types, fields, methods or parameters where an annotation is not allowed, i.e., java.lang.annotation.Target does not include that target (type, field, etc.)

openapi-processor can’t check the allowed targets of java.lang.annotation.Target directly, because the annotations are not available on its classpath. They are just strings.

To solve this issue, it is now possible to configure the allowed targets of an annotation. The mapping.yaml has a new section annotation-targets. This is a map from annotation name to set of allowed targets.

mapping.yaml
openapi-processor-mapping: v16
options:
  package-name: pkg

map:
  types:
    - type: Foo @ lombok.Builder

annotation-targets:
  # possible entries 'type', 'field', 'method' & 'parameter'
  lombok.Builder: ['type', 'method']

You don’t have to explicitly add lombok.Builder. openapi-processor has a default list of know annotations. If there are other common annotations, I can add them to the default list.

The default list is quite short at the moment:

current default list

  • lombok.Builder: ['type', 'method']

December 2025

  • openapi-processor-gradle 2025.1

This version will only work with gradle 7+, use 2021.3 with gradle 5.5+

minimum gradle version minimum java version

8.2+ with kotlin dsl
7.2+ with groovy dsl

17

migrated to kotlin

the plugin is now (mostly) written in kotlin

gradle 9 compatibility

fixed all deprecated warnings for gradle 9

configuration improvements

it is now possible to

  • pass a file path created by layout to apiPath:

    // kotlin dsl
     openapiProcessor {
        apiPath(layout.projectDirectory.file("src/api/openapi.yaml"))
    }
  • pass a directory path created by layout to targetDir:

    // kotlin dsl
     openapiProcessor {
        process("spring") {
            targetDir(layout.buildDirectory.dir("openapi"))
        }
    }
  • pass a file to the mapping property:

    // kotlin dsl
     openapiProcessor {
        process("spring") {
            prop("mapping", layout.projectDirectory.file("src/api/mapping.yaml"))
        }
    }
  • compile generated code without dependsOn()

    // kotlin dsl
    sourceSets {
        create("api") {
            resources {
                // add api resources
                srcDir(layout.projectDirectory.dir("src/api"))
            }
        }
    
        afterEvaluate {
            main {
                java {
                    // add generated files
                    srcDir(tasks.named("processSpring"))
                }
            }
        }
    }

detect mapping.yaml changes outside of apiDir

Gradle does now include the mapping.yaml in the up-to-date check if it is outside the apiDir.

October 2025

  • openapi-processor-spring/micronaut 2025.5

OpenAPI 3.2 support

this version adds OpenAPI 3.2 support. It does support the following OpenAPI 3.2 features:

  • $self

  • responses/…​/…​/…​/itemSchema stream response (#314)

  • paths/…​/query HTTP method (#313)

  • paths/additionalOperations/…​ custom HTTP methods (#313)

result mapping must not be a generic type

So far the result mapping required a generic wrapper class, like Spring ResponseEntity<>. It no longer has to be a generic wrapper. It allows a plain replacement class that will be used as the return type.

The current result mapping automatically assumes a generic wrapper class by default. To use a plain replacement type, i.e. no wrapper, it expects an arrow mapping:

plain ⇒ {target type}

which means, instead of the plain schema described in the OpenAPI response, use target type.

mapping.yaml
openapi-processor-mapping: v15

options:
  package-name: io.openapiprocessor.generated

map:

  paths:
    /foo:
       # standard, result wrapper with generic parameter
       result: org.springframework.http.ResponseEntity

       # new, mapping of plain replacement class
       result: plain => io.stream.Response

This allows to return types as Spring SseEmitter or Spring StreamingResponseBody from an endpoint implementation.

generate unreferenced schemas

By default, openapi-processor will only generate DTOs for schemas that are referenced by an endpoint.

It is now possible to generate DTOs from unreferenced schemas under component/schemas by setting the model-unreferenced option to true.

mapping.yaml
openapi-processor-mapping: v15

options:
  package-name: io.openapiprocessor.generated

  # default is false
  model-unreferenced: true

dependency updates

  • updated (internal) OpenAPI parser to 2025.5 (OpenAPI 3.2 support)

  • updated swagger parser to 2.1.34

September 2025

  • openapi-processor-spring/micronaut 2025.4

incompatibility when using enum-type: framework and format-code

when using enum-type: framework openapi-processor created a package-info.java which failed to format if format-code: true|google was enabled.

use $ref filename without json pointer as class name

the processor did create the class name of FooResponse based on the location as FooGetResponse200.java if the $ref did not have a json pointer.

It does now use the file name as class name if the $ref does not have a json pointer, i.e. in the example below the class name will be FooResponse instead of FooGetResponse200.

openapi.yaml
openapi: 3.1.0
info:
  title: get class name from file name
  version: 1.0.0

paths:
  /foo:
    get:
      responses:
        '200':
          description: the foo result
          content:
            application/json:
                schema:
                  $ref: 'FooResponse.yaml'
FooResponse.yaml
title: Foo Schema
type: object
properties:
  bar:
    type: string

mapping and bean validation annotations

the processor could produce invalid code that does not compile when combining a mapping with bean validation.

An OpenAPI (like the one below) with an integer parameter and bean validation enabled would add @DecimalMin & @DecimalMax annotations to the parameter in the generated code.

openapi.yaml
openapi: 3.1.0
info:
  title: drop bean validation annotation if mapped to unsupported type
  version: 1.0.0

paths:
  /foo:
    get:
      parameters:
        - in: query
          name: year
          schema:
            type: integer
            format: year
            minimum: 1970
            maximum: 2099

This is an issue if the parameter type is mapped to a different Java type.

openapi.yaml
openapi-processor-mapping: v18

options:
  package-name: generated
  bean-validation: jakarta

map:
  types:
    - type: integer:year => java.time.Year

In the example to java.time.Year, because both annotations are not supported on java.time.Year.

To fix this, the processor does not add it if it is not allowed.

In case the target type is not recognized automatically (and the annotations are dropped), for example on a custom java.lang.Number implementation, it is possible to tell the processor that an annotation is valid on that type.

mapping.yaml
openapi-processor-mapping: v18

options:
# ...

map:
# ...

bean-validation:
  jakarta.validation.constraints.DecimalMin:
    - other.CustomInteger
  jakarta.validation.constraints.DecimalMax:
    - other.CustomInteger

dropping parameters by OpenAPI name

dropping parameters did only work for parameters names if the OpenAPI name was identical to the Java name, i.e. no special characters. It does now handle special characters like -, _ or ` ` (space).

mapping.yaml
# ...

map:
 paths:
   /foo:
     get:
       parameters:
         - drop: foo-Param
         - drop: barParam

June 2025

  • openapi-processor-spring/micronaut 2025.3

package-names from location

it may not behave nicely if the expected configuration requirements are not met. It also works only with the INTERNAL OpenAPI parser (which is the default).

The package-names from location feature allows the processor to create package names based on the file location of $ref’erenced parts of the OpenAPI description.

This gets enabled by setting the package-names:location option.

openapi-processor-mapping: v13

options:
  # package-name: io.openapiprocessor.sample (1)

  package-names:
    base: io.openapiprocessor.openapi (2)
    # this enables package generation from the endpoint $ref file location
    location:  io.openapiprocessor.samples (3)
1 the shortcut for setting package-names.base. If location based packages should be used, setting package-names.base is preferred.
2 this is the base package for all generated code. This is identical to the current behaviour (i.e. package-name). Any file the is not below package-names.location will be generated with this as the base package.
3 package-name.location is the parent package name of the project’s target packages. If the processor finds a file ref’erenced from the main OpenAPI in a subpackage of package-name.location the generated sources will be generated with that package.

Here is an example layout to show what this is about.

The OpenAPI description of the endpoint foo is placed into the package where it will be implemented. The generated interface and resources get the package io.openapiprocessor.samples.foo.

The example shows only the controller implementation, but it could also contain service and repositories used to handle the foo endpoint. That way, everything related to that endpoint is in one place.

sample
\---- src
      +---- api
      |     +---- mapping.yaml
      |     \---- openapi.yaml
      \---- main
            +---- kotlin
            |     +---- io
            |     |     \---- openapiprocessor
            |     |           \---- samples
            |     |                 +---- foo
            |     |                 |     +---- FooController.kt
            |     |                 |     +---- foo.yaml
            |     |                 |     \---- resources.yaml
            |     |                 \---- bar
            |     |                       \---- ...
            \---- resources
                  \---- application.properties

The main OpenAPI file will look something like this:

# openapi.yaml
openapi: 3.1.0
info:
  title: openapi-processor sample api
  version: 1.0.0

servers:
  - url: "https://openapiprocessor.io/{path}"
    variables:
      path:
        default: api

paths:
  /foo:
    $ref: '../main/kotlin/io/openapiprocessor/samples/foo/foo.yaml' (1)
1 foo.yaml (the path item of foo) is $ref`erenced from the main OpenAPI description.
# io/openapiprocessor/samples/foo/foo.yaml
post:
  tags:
    - foo
  summary: echo a Foo.
  description: simple sample endpoint
  requestBody:
    $ref: 'resources.yaml#/FooBody'
  responses:
    '200':
      description: foo
      content:
        application/json:
          schema:
            $ref: 'resources.yaml#/Foo' (1)
1 and $references the resource.yaml in the same folder that describes the payload resource.

The package name of the foo endpoint files is io.openapiprocessor.samples.foo and the nearest parent package is io.openapiprocessor.samples. This is then the package-name option value.

It is possible to use io.openapiprocessor or even io as the parent package.

The generated files will still go to the output folder of the used build tool. No change there apart from the package names.

See also the spring-mvc-boot-4-packages-kt for an example setup.

generate response status annotation

the processor does now automatically generate a response status annotation for success responses (i.e., 2xx) not equal to 200.

This will conflict with manually added response status annotations.

To keep the old behavior, i.e., no automatically added annotations, set result-status: false on the global mapping level.

It is configured by adding it to the mapping section of the configuration file. It is available on all levels, i.e., global, endpoint and endpoint method.

openapi-processor-mapping: v18

options:
    # ...

map:
  # result-status: true is the default
  # setting it to false on the global level disables it
  result-status: false

  paths:
    # enable it for a specific endpoint
    /foo:
      result-status: true

      # ... or for a specific method of an endpoint
      #get:
      #  result-status: true

example:

openapi: 3.1.0
info:
  title: sample api
  version: 1.0.0

paths:
  /nop:
    get:
      tags:
        - nop
      summary: response status
      description: adds status for success other than 200
      responses:
        '204':
          description: no content

generates (with the framework-specific annotations):

package generated.api;

import annotation.Mapping;
import annotation.Status;
import generated.support.Generated;

@Generated(value = "openapi-processor-core",  version = "test")
public interface NopApi {

  /** response status adds status for success other than 200 */
  @Status(204)
  @Mapping("/nop")
  void getNop();

}

April 2025

  • openapi-processor-spring/micronaut 2025.2

support endpoint with different responses for different status codes

For the example below, versions before 2025.2 would pick Bar (the last response) as the return type for the getFooApplicationJson() endpoint method. This doesn’t work because the method must be able to return Foo or Bar.

To make this work it will now use Object as return type.

openapi: 3.1.0
info:
  title: test multiple success responses
  version: 1.0.0

paths:
  /foo:
    get:
      description: endpoint with multiple success responses
      responses:
        '200':
          description: success
          content:
            application/json:
                schema:
                  $ref: '#/components/schemas/Foo'
        '202':
          description: another success
          content:
            application/json:
                schema:
                  $ref: '#/components/schemas/Bar'

components:
  schemas:

    Foo:
      type: object
      properties:
        foo:
          type: string

    Bar:
      type: object
      properties:
        bar:
          type: string

marker interface for responses

The previous fix handles multiple response objects by using Object as the result type. An Object return type is not very descriptive. It is impossible to know from the interface which results are possible.

To improve on that situation, the processor can generate a marker interface that is more descriptive and helps with navigation in the IDE.

Generation of the marker interface is enabled by adding the response-interface option:

openapi-processor-mapping: v12

options:
  package-name: ...
  # ...
  response-interface: true

The marker interface is an empty interface, and its name is derived from the http method, path and content type to create a unique name.

If the response type (e.g., Foo from the above example OpenAPI) is used on multiple endpoints with multiple success response statuses, it will implement multiple marker interfaces.

package generated.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;

@Generated(value = "openapi-processor-core", version = "test")
public class Foo implements GetFooApplicationJsonResponse /* , .. more interfaces if Foo is used on multiple endpoints */ {

    @JsonProperty("foo")
    private String foo;

    // ...
}

That way it is possible to find the possible result type by navigating to the implementations of the marker interface.

drop OpenAPI parameter

It is now possible to drop a parameter given in the OpenAPI description from the generated code. This may be useful if a parameter is, for example, handled by a request filter and therefore is not needed in the endpoint method anymore.

To drop a parameter add a parameters/drop entry with the name of the parameter to drop it:

openapi-processor-mapping: v12

options:
  package-name: generated

map:
  paths:
    /foo:
      parameters:
        - drop: foo

Even if it is possible to add it at the global level, it is best used at the endpoint level.

result-style

the result-style option is now handled on all levels (global, endpoint, http method) and not just on the global level.

formatter selection

the processor didn’t use the new formatter selection, it does now properly handle google & eclipse (no need to for extra jdk configuration) values.

openapi-processor-mapping: v12
options:
  package-name: # ...
  format-code: false      # disable code formatter
  format-code: true       # use default google code formatter
  format-code: google     # use google code formatter, i.e. the same as "true"
  format-code: eclipse    # use eclipse code formatter

March 2025

  • openapi-processor-spring/micronaut 2025.1

do not generate accessors of pojos

It is now possible to disable generation of accessor methods on pojo dtos. The properties are still private. This is only useful in combination with an object annotation mapping that adds the accessors. For example lombok.Getter & lombok.Setter.

openapi-processor-mapping: v11

options:
  package-name: generated
  model-type: default           # i.e. pojo
  model-accessors: false        # only used if model-type is default

map:
  types:
    - type: object @ lombok.Getter
    - type: object @ lombok.Setter
package io.openapiprocessor.openapi.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.openapiprocessor.openapi.support.Generated;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Generated(value = "openapi-processor-spring")
public class Foo {

    @JsonProperty("id")
    private UUID id;

}

schema mappings

It is now possible to restrict annotation mappings to schema properties by using schema level mappings. Schema mappings are only supported at the global level:

openapi-processor-mapping: v11

options:
  package-name: generated
  format-code: false

map:
  types:
    - type: integer:year => java.time.Year

  schemas:
    - type: integer:year @ com.fasterxml.jackson.annotation.JsonFormat(shape = com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER_INT)

The schema mapping will tell the processor to apply the annotation only on dto properties:

package generated.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;
import java.time.Year;

@Generated(value = "openapi-processor-core", version = "latest")
public class Foo {

    @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT)
    @JsonProperty("year")
    private Year year;

    // ...
}

and not to the api endpoint method parameter:

 package generated.api;

 import generated.model.Foo;
 import generated.support.Generated;
 import java.time.Year;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestParam;

 @Generated(value = "openapi-processor-core", version = "test")
 public interface Api {

     @GetMapping(path = "/foo", produces = {"application/json"})
     Foo getFoo(@RequestParam(name = "year", required = false) Year year);

 }

alternative code formatter

experimental (whatever is the use of formatting the generated code anyway.. ;-)

the current code formatter google-java-format uses internal java classes which requires additional configuration.

To avoid this additional configuration openapi-processor now supports the eclipse code formatter.

To support this the format-code option accepts two new values: google and eclipse.

openapi-processor-mapping: v11
options:
  package-name: # ...
  format-code: false      # disable code formatter
  format-code: true       # use default google code formatter
  format-code: google     # use google code formatter, i.e. the same as "true"
  format-code: eclipse    # use eclipse code formatter

javadoc improvement

improved javadoc generation for $ref with description.

# OpenAPI document

components:
  schemas:

    Foo:
      description: >
        this is the *Foo* schema description
      type: object
      properties:
        foo-bar:
          description: >
            *property* description
          type: string
        enum:
          description: >                                     (1)
            enum *property* description
          $ref: '#/components/schemas/FooEnum'

    FooEnum:
        description: "this is an *enum* description"
        type: string
        enum: ['foo', 'bar']

javadoc generation now handles a description (<1>) at $ref elements.

For the given OpenAPI description above the pojo for Foo will now look like this

package generated.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;

/**
 * this is the <em>Foo</em> schema description
 */
@Generated(value = "openapi-processor-core", version = "test")
public class Foo {

    /**
     * <em>property</em> description
     */
    @JsonProperty("foo-bar")
    private String fooBar;

    /**
     * enum <em>property</em> description
     */
    @JsonProperty("enum")
    private FooEnum aEnum;

    // ...
}

And for the record variant:

package generated.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;

/**
 * this is the <em>Foo</em> schema description
 *
 * @param fooBar <em>property</em> description
 * @param aEnum enum <em>property</em> description
 */
@Generated(value = "openapi-processor-core", version = "test")
public record Foo(
    @JsonProperty("foo-bar")
    String fooBar,

    @JsonProperty("enum")
    FooEnum aEnum
) {}

dependency updates

  • updated (internal) OpenAPI parser to 2025.1 (was 2024.7)

  • updated com.fasterxml.jackson:jackson-bom to 2.18.2 (was 2.18.1)