[OpenAPI] Custom type annotated with Schema and type = SchemaType.ARRAY and implementation = MyKlass.class inlines implementation properties
Describe the bug
Given a custom type (let's say collection type in a 3rd party lib) field that is annotated, e.g.
// Pretend this is something coming from a 3rd party lib, like Guava
// ImmutableList
public record ImmutableList<T>(List<T> items) {
}
public record Greeting(String message) {
}
public record Response(
@Schema(
description = "A list of messages",
implementation = Greeting.class,
type = SchemaType.ARRAY)
ImmutableList<Greeting> greetings) {
}
The resulting OpenAPI file generated via https://quarkus.io/extensions/io.quarkus/quarkus-smallrye-openapi/ has an in-lined form.
Expected behavior
I would expect the Greeting class to have its own definition under schemas and for the items of the greeting property of Response to reference it
components:
schemas:
Greeting:
type: object
properties:
message:
type: string
Response:
type: object
properties:
greetings:
description: A list of messages
type: array
items:
$ref: "#/components/schemas/Greeting"
# Elided
Actual behavior
The Greeting class definition gets inlined directly into the items :
components:
schemas:
Response:
type: object
properties:
greetings:
description: A list of messages
type: array
items:
type: object
properties:
message:
type: string
paths:
/hello:
get:
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/Response"
summary: Hello
tags:
- Greeting Resource
How to Reproduce?
https://github.com/lloydmeta/quarkus-open-api-custom-items
Output of uname -a or ver
Darwin Mac 24.4.0 Darwin Kernel Version 24.4.0: Fri Apr 11 18:33:39 PDT 2025; root:xnu-11417.101.15~117/RELEASE_ARM64_T6020 arm64
Output of java -version
openjdk 21.0.6 2025-01-21
Quarkus version or git rev
3.22.1
Build tool (ie. output of mvnw --version or gradlew --version)
Gradle 8.13
Additional information
No response
/cc @EricWittmann (openapi), @MikeEdgar (openapi), @phillip-kruger (openapi)
@lloydmeta this is a common issue. The correct way to indicate that a class should be used as the items of an array is to specify implementation = Greeting[].class rather than implementation = Greeting.class. Note, the [].
If the previous suggestion doesn't work, you can try adding @Schema to the Greeting class also.
@lloydmeta this is a common issue. The correct way to indicate that a class should be used as the
itemsof an array is to specifyimplementation = Greeting[].classrather thanimplementation = Greeting.class. Note, the[].
@MikeEdgar Gotcha. FWIW, I got that type+implementation idea from this comment https://github.com/microprofile/microprofile-open-api/issues/362#issuecomment-511429042,
I tried your suggestion and it doesn't work.. it results in something stranger:
schemas:
Response:
type: object
properties:
greetings:
description: A list of messages
type: array
items:
type: array
items:
type: object
properties:
message:
type: string
https://github.com/lloydmeta/quarkus-open-api-custom-items/commit/28bfb8d96d159cc7a7e8e8312131e6e5612043ed
If the previous suggestion doesn't work, you can try adding
@Schemato theGreetingclass also.
So, there are limits to this: I'm specifically looking to be able to use this for models that use 3rd party collections, like ImmutableList from Guava, for instance, where I can't add an annotation to those classes.
In any case, I gave it a shot in https://github.com/lloydmeta/quarkus-open-api-custom-items/commit/9526bd457ad92acf35e848d7c50eff0f5dd34d1a; still doesn't seem to help.
components:
schemas:
Greeting:
description: A greeting message
type: object
properties:
message:
type: string
description: The message to be displayed
ImmutableList:
type: array
Response:
type: object
properties:
greetings:
description: A list of messages
type: array
items:
type: array
items:
description: A greeting message
type: object
properties:
message:
type: string
description: The message to be displayed
The items just inlines+repeats the Greeting definition....
This should get things closer to want you expect. Note that ImmutableList should likely extend/implement List or Iterable for it to behave how it would using some collection library.
// Pretend this is something coming from a 3rd party lib, like Guava
// ImmutableList
public class ImmutableList<T> extends java.util.ArrayList<T> {
public ImmutableList(List<T> items) {
super(items);
}
}
@Schema(description = "A greeting message")
public record Greeting(
@Schema(description = "The message to be displayed") String message) {
}
public record Response(
@Schema(
description = "A list of messages") ImmutableList<Greeting> greetings) {
}
Result - it does lead to an intermediate ImmutableListGreeting schema, but it's functionally the same as your expected result.
components:
schemas:
Greeting:
type: object
description: A greeting message
properties:
message:
type: string
description: The message to be displayed
ImmutableListGreeting:
type: array
items:
$ref: "#/components/schemas/Greeting"
Response:
type: object
properties:
greetings:
$ref: "#/components/schemas/ImmutableListGreeting"
type: array
description: A list of messages
Thanks @MikeEdgar , that's a useful workaround, and I suppose it's interesting that it's functionally the same.
I think having it on hand would lower the priority of this issue, but I'd love to see if we could get to something that would give the expected, and I hope not unreasonable, behaviour.