Reputation: 37106
I have endpoint which accepts BaseDto:
@JsonTypeInfo(...)
@JsonSubTypes({
@JsonSubTypes.Type(...),
...
})
@RequestMapping(
method = RequestMethod.POST,
value = "/path",
produces = { "application/json" },
consumes = { "application/json" }
)
fun foo(@Valid @RequestBody dto:BaseDto ):ResponseEntity<?> {
...
}
Dto has inheritance hierarchy:
class Child1Dto : BaseDto {
class Child2Dto : BaseDto {
class Child3Dto : BaseDto {
...
So in the controller I have Child1Dto
or Child2Dto
or Child3Dto
Also I have Model BaseModel
class Child1Model : BaseModel {
class Child2Model : BaseModel {
class Child3Model : BaseModel {
Now I have following code:
fun BaseDto.toModel(): BaseModel =
when (this) {
is Child1Dto -> Child1Model(/*no args*/)
is Child2Dto -> Child2Model(/*no args*/)
is Child3Dto -> Child3Model(/*some args from dto*/)
else -> throw IllegalStateException("Unknown dto type ${this::class}")
}
I don't like this code because of
Is there better way to rewrite this code ?
Upvotes: 0
Views: 112
Reputation: 32
You can do that. You will have to have a switch or map somewhere, but it can be hidden from the controller.
Centralized mapper:
object DtoMapperRegistry {
private val mappers: Map<Class<out BaseDto>, DtoToModelMapper<out BaseDto, out BaseModel>> = mapOf(
Child1Dto::class.java to Child1DtoMapper(),
Child2Dto::class.java to Child2DtoMapper(),
Child3Dto::class.java to Child3DtoMapper()
)
@Suppress("UNCHECKED_CAST")
fun <D : BaseDto, M : BaseModel> map(dto: D): M {
val mapper = mappers[dto::class.java]
?: throw IllegalStateException("No mapper found for ${dto::class}")
return (mapper as DtoToModelMapper<D, M>).map(dto)
}
}
To use it you just need a single call:
val model = DtoMapperRegistry.map(dto)
Upvotes: 0
Reputation: 1342
add a new method toModel
for decouple the DTO from the Model
abstract class BaseDto {
abstract fun toModel(): BaseModel
}
and then implement the Method in Each Child Class
class Child1Dto : BaseDto() {
override fun toModel(): BaseModel {
return Child1Model()
}
}
class Child2Dto : BaseDto() {
override fun toModel(): BaseModel {
return Child2Model()
}
}
class Child3Dto : BaseDto() {
override fun toModel(): BaseModel {
return Child3Model(/* map necessary arguments here */)
}
}
Upvotes: 0
Reputation: 4171
You can check out @JsonSubTypes that allow polymorphic deserialization
So for example you can define a parent:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "fooProperty", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = ChildClassOne.class, name = "foo"),
@JsonSubTypes.Type(value = ChildClassTwo.class, name = "bar"),
@JsonSubTypes.Type(value = ChildClassThree.class, name = "baz")
}) public abstract class BaseDto {
public String fooProperty;
}
then based on the value of fooProperty
, JSON will be deserialized into the coresponding child class
Upvotes: 0