Skip to content

Serialization to openapi of org.springframework.data.domain.Sort is wrong for Spring Boot >2.x #2725

Closed
@derXear

Description

@derXear

The serialization of org.springframework.data.domain.Sort seems to be wrong with v2.6.0

Minimal reproducable example

With the following versions

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.2.10</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <dependency>
	    <groupId>org.springdoc</groupId>
	    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
	    <version>2.6.0</version>
    </dependency>
    ...
</dependencies>

and a rest controller like this

@RestController
@RequestMapping(value = {"/api/test"})
public class TestController {

    @GetMapping
    public Page<String> getTestItemsPaged(
      @RequestParam(defaultValue = "0") int page,
      @RequestParam(defaultValue = "5") int size
    ) {
        String[] items = new String[20];
        for (int i = 0; i < 20; i++) {
            items[i] = String.valueOf(i);
        }
        int offset = page * size;
        int limit = Math.min(size, items.length - offset);
        List<String> result = Arrays.asList(items).subList(offset, offset + limit);
        return new PageImpl<>(result, PageRequest.of(page, size), items.length);
    }
}

when starting the application and checking the endpoint: http://localhost:8080/v3/api-docs

we get this

{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenAPI definition",
    "version": "v0"
  },
  "servers": [
    {
      "url": "http://localhost:8080",
      "description": "Generated server url"
    }
  ],
  "paths": {
    "/api/test": {
      "get": {
        "tags": [
          "test-controller"
        ],
        "operationId": "getTestItemsPaged",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/PageString"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PageString": {
        "type": "object",
        "properties": {
          "totalPages": {
            "type": "integer",
            "format": "int32"
          },
          "totalElements": {
            "type": "integer",
            "format": "int64"
          },
          "pageable": {
            "$ref": "#/components/schemas/PageableObject"
          },
          "first": {
            "type": "boolean"
          },
          "last": {
            "type": "boolean"
          },
          "size": {
            "type": "integer",
            "format": "int32"
          },
          "content": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "number": {
            "type": "integer",
            "format": "int32"
          },
          "sort": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SortObject"
            }
          },
          "numberOfElements": {
            "type": "integer",
            "format": "int32"
          },
          "empty": {
            "type": "boolean"
          }
        }
      },
      "PageableObject": {
        "type": "object",
        "properties": {
          "paged": {
            "type": "boolean"
          },
          "pageNumber": {
            "type": "integer",
            "format": "int32"
          },
          "pageSize": {
            "type": "integer",
            "format": "int32"
          },
          "offset": {
            "type": "integer",
            "format": "int64"
          },
          "sort": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SortObject"
            }
          },
          "unpaged": {
            "type": "boolean"
          }
        }
      },
      "SortObject": {
        "type": "object",
        "properties": {
          "direction": {
            "type": "string"
          },
          "nullHandling": {
            "type": "string"
          },
          "ascending": {
            "type": "boolean"
          },
          "property": {
            "type": "string"
          },
          "ignoreCase": {
            "type": "boolean"
          }
        }
      }
    }
  }
}

but calling the Rest endpoint

curl --request GET \
  --url 'http://localhost:8080/api/test?size=10'

results in this response

{
  "content": [
    "0",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9"
  ],
  "pageable": {
    "pageNumber": 0,
    "pageSize": 10,
    "sort": {
      "sorted": false,
      "empty": true,
      "unsorted": true
    },
    "offset": 0,
    "paged": true,
    "unpaged": false
  },
  "last": false,
  "totalPages": 2,
  "totalElements": 20,
  "first": true,
  "size": 10,
  "number": 0,
  "sort": {
    "sorted": false,
    "empty": true,
    "unsorted": true
  },
  "numberOfElements": 10,
  "empty": false
}

There is a mismatch for the representation of the sort object in the generated swagger json. Since Spring Boot 2.x the response for sort is an object and not an array anymore.

I guess the issue was introduced with a fix for #2447 which will only work for Spring Boot 1.x but not for Spring Boot 2.x anymore. See the changes in more detail here:
https://www.wimdeblauwe.com/blog/2018/2018-06-10-pageimpl-json-serialization-with-spring-boot-2/

Metadata

Metadata

Assignees

No one assigned

    Labels

    incompleteincomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions