swagger-core
swagger-core copied to clipboard
Incorrect mapping of sub-classes when nested as member of another sub-class
Note: This issue was posted in springdoc-openapi first. It was closed, since the issue seams to be located in swagger-core.
Describe the bug
- Inherited sub-classes are not mapped correctly if they are nested in a sub-class that by itself is derived from a super class.
- I'd expect the mapping to be
onOf()which is true for the nested class itself, but not for the member. - See the example to make things clear:
To Reproduce Steps to reproduce the behavior:
- Spring Boot starter parent, version 3.0.2
- Spring Doc modules starter-webmvc-ui and starter-common in version 2.0.2.
Here's an example controller:
@RestController
@RequestMapping(path = "/v1/test", produces = MediaType.APPLICATION_JSON_VALUE)
public class TestOpenApiError {
@GetMapping("/a")
public A testGetA() {
return null;
}
@GetMapping("/b")
public B testGetB() {
return null;
}
@GetMapping("/c")
public C testGetC() {
return null;
}
@Schema(
description = "Some out class",
subTypes = { A1.class, A2.class, },
discriminatorProperty = "@type",
discriminatorMapping = {
@DiscriminatorMapping(schema = A1.class, value = "A1"),
@DiscriminatorMapping(schema = A2.class, value = "A2"),
}
)
public class A {}
public class A1 extends A {}
public class A2 extends A {
public List<? extends B> listOfBs;
}
@Schema(
description = "Some nested class",
subTypes = { B1.class, B2.class, },
discriminatorProperty = "@type",
discriminatorMapping = {
@DiscriminatorMapping(schema = B1.class, value = "B1"),
@DiscriminatorMapping(schema = B2.class, value = "B2"),
}
)
public class B {}
public class B1 extends B {}
public class B2 extends B {}
public class C {
public List<? extends B> listOfBs;
}
}
I have three classes A, B, and C. A1 and A2 are derived from A. B1 and B2 are derived from B.
B as list is member of A2 (nested in a class that is derived, too) and B as list is member of C (not nested in an inheritance)
This is the generated outcome fragment for this as yml
B:
type: object
description: Some nested class
discriminator:
propertyName: '@type'
mapping:
B1: '#/components/schemas/B1'
B2: '#/components/schemas/B2'
B1:
type: object
allOf:
- $ref: '#/components/schemas/B'
B2:
type: object
allOf:
- $ref: '#/components/schemas/B'
C:
type: object
properties:
listOfBs:
type: array
items:
oneOf:
- $ref: '#/components/schemas/B'
- $ref: '#/components/schemas/B1'
- $ref: '#/components/schemas/B2'
A:
type: object
description: Some out class
discriminator:
propertyName: '@type'
mapping:
A1: '#/components/schemas/A1'
A2: '#/components/schemas/A2'
A1:
type: object
allOf:
- $ref: '#/components/schemas/A'
A2:
type: object
allOf:
- $ref: '#/components/schemas/A'
- type: object
properties:
listOfBs:
type: array
items:
$ref: '#/components/schemas/B'
When B is member of C the generation is as expected. Is maps to oneOf. As expected:
C:
type: object
properties:
listOfBs:
type: array
items:
oneOf:
- $ref: '#/components/schemas/B'
- $ref: '#/components/schemas/B1'
- $ref: '#/components/schemas/B2'
When B is member of A2 it just refers to an array of type B. No oneOf mapping:
A2:
type: object
allOf:
- $ref: '#/components/schemas/A'
- type: object
properties:
listOfBs:
type: array
items:
$ref: '#/components/schemas/B'
Expected behavior
I'd expect A2 to me mapped like this:
A2:
type: object
allOf:
- $ref: '#/components/schemas/A'
- type: object
properties:
listOfBs:
type: array
items:
oneOf:
- $ref: '#/components/schemas/B'
- $ref: '#/components/schemas/B1'
- $ref: '#/components/schemas/B2'
Maybe I'm doing something wrong. Maybe somebody can confirm, that this is a real issue. Thx for any help.
I came across this issue searching for something similar. In my case B is abstract and I wanted to show oneOf B1 and B2.
I was able to show the correct options this by adding this @ArraySchema
public class A2 extends A {
@ArraySchema(schema = @Schema(implementation = Objects.class, oneOf = {B1.class, B2.class}))
public List<? extends B> listOfBs;
}
I came across this issue searching for something similar. In my case
Bis abstract and I wanted to show oneOfB1andB2. I was able to show the correct options this by adding this@ArraySchemapublic class A2 extends A { @ArraySchema(schema = @Schema(implementation = Objects.class, oneOf = {B1.class, B2.class})) public List<? extends B> listOfBs; }
That's the best solution, but you have to remember, that fields from parent abstract class won't be included in docs.