I have a Spring-Boot project with an API-Endpoint, that allows the consumer to send an object, where at least
one field use polymorphism to be one of multiple data classes. With the help of springdoc-openapi-starter-webmvc-ui it
automatically creates a OpenAPI-Config, so that consumers can automatically generate classes in their language.
The polymorph is giving me a headache and I just can't figure it out. My goal is to generate a OpenAPI-Config, that will
generate the data classes as they are in my backend.
Here is the general Set-Up (attached are the generated components of the open-api-config):
@RestController
public class PersonController {
@PostMapping("person")
public String postPerson(@RequestBody PersonDto dto) {
return "Some Value";
}
}
public record PersonDto(
String name,
AnimalDto pet
) {
}
public interface AnimalDto {
}
public record DogDto(
String name,
String barkSound
) implements AnimalDto {
}
public record CatDto(
String name,
String meowSound
) implements AnimalDto {
}
OpenAPI Annotations
When I use the OpenAPI annotation, then I get a configuration as I expect it and when generating the classes in Java, I
get the exakt same structure of classes as in my backend.
The problem is that Jackson has no idea how to handle these classes, when they are about to be deserialized. So I will
need the Jackson annotations for polymorph.
@Schema(
oneOf = {
DogDto.class,
CatDto.class
},
discriminatorProperty = "discriminatorType"
)
public interface AnimalDto {
String discriminatorType();
}
// Add discriminatorType
public record DogDto(
String discriminatorType,
String name,
String barkSound
) implements AnimalDto {
}
// Add discriminatorType
public record CatDto(
String discriminatorType,
String name,
String meowSound
) implements AnimalDto {
}
Jackson
So when I use the Jackson annotation I'm able to handle the polymorph, and it will generate a OpenAPI
configuration based on them, but it's a little off. The PersonDto will use an inline oneOf for the two
implementations, instead of using a reference to the AnimalDto. When generating with that configuration, it leads to
an abstract AnimalDto class and a PersonDtoPet interface.
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "discriminatorType",
visible = true
)
@JsonSubTypes({
@JsonSubTypes.Type(value = CatDto.class),
@JsonSubTypes.Type(value = DogDto.class)
})
public interface AnimalDto {
}
How?
Using both annotations at the same time results in even weirder results, which even creates critical errors. So I wonder
what is the solution to that? I thought this would be a normal use-case and be done in minutes. But now I sit here for hours already.
If anybody could help out, that would be really awesome. Thanks for reading!