Lord Tkay
Lord Tkay
Explore posts from servers
JCHJava Community | Help. Code. Learn.
Created by Lord Tkay on 4/24/2025 in #java-help
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!
4 replies
JCHJava Community | Help. Code. Learn.
Created by Lord Tkay on 5/29/2024 in #java-help
Best Practice for DTO-Mapping in Spring
Greetings, currently I'm part of rewriting an super old web-spring-boot-application in my company and would like to detach the request object from the business logic, because it's used like an Super-Object for everything. And there is this concept of DTOs, which seem to be the best practice for request objects anyways. Is there a best practice on how to map the DTO to my business-objects and the other way around in Spring-Boot-3?
7 replies
JCHJava Community | Help. Code. Learn.
Created by Lord Tkay on 2/25/2024 in #java-help
ConditionalOnBean without creating own config for every bean
I got a @Configuration that creates a bean, lets say Mandatory, when a specific property is set. This bean is used by many other beans that are on other files and won't work without given bean. So I tried to add @ConditionalOnBean(value = [Mandatory::class]) on the classes that will need that bean. But that does not seem to work. The condition only worked when I created the beans manually inside of the same configuration. Am I doing something wrong or do I really need to manually create all beans, when I wanna work with conditions?
@Configuration
class MyConfig() {
@Bean
@ConditionalOnProperty(["my.property.activate"])
fun mandatory(): Mandatory {
return Mandatory()
}

// This works
@Bean
@ConditionalOnBean(value = [Mandatory::class])
fun someClass(mandatory: Mandatory): SomeClass {
return SomeClass(mandatory)
}
}
@Configuration
class MyConfig() {
@Bean
@ConditionalOnProperty(["my.property.activate"])
fun mandatory(): Mandatory {
return Mandatory()
}

// This works
@Bean
@ConditionalOnBean(value = [Mandatory::class])
fun someClass(mandatory: Mandatory): SomeClass {
return SomeClass(mandatory)
}
}
// This does not work
@Component
@ConditionalOnBean([Mandatory::class])
class SomeClass(var mandatory: Mandatory) {
//...
}
// This does not work
@Component
@ConditionalOnBean([Mandatory::class])
class SomeClass(var mandatory: Mandatory) {
//...
}
14 replies