Polymorphism in OpenAPI & Jackson

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";
}

}
@RestController
public class PersonController {

@PostMapping("person")
public String postPerson(@RequestBody PersonDto dto) {
return "Some Value";
}

}
public record PersonDto(
String name,
AnimalDto pet
) {
}
public record PersonDto(
String name,
AnimalDto pet
) {
}
public interface AnimalDto {
}
public interface AnimalDto {
}
public record DogDto(
String name,
String barkSound
) implements AnimalDto {
}
public record DogDto(
String name,
String barkSound
) implements AnimalDto {
}
public record CatDto(
String name,
String meowSound
) 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();
}
@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 DogDto(
String discriminatorType,
String name,
String barkSound
) implements AnimalDto {
}
// Add discriminatorType
public record CatDto(
String discriminatorType,
String name,
String meowSound
) 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 {
}
@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!
1 Reply
JavaBot
JavaBot7d ago
This post has been reserved for your question.
Hey @Lord Tkay! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here. 💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.

Did you find this page helpful?