Class 'TaskPayload' is not registered for polymorphic serialization in the scope of 'EventPayload' - kotlinx.serialization

I have a simple sealed class and a concrete class using #Serializable. However when compiling I always end up with an error.
// EventPayload.kt
#Serializable
sealed class EventPayload {
val type: String
get() = this::class.simpleName!!
}
// TaskPayload.kt
#Serializable
data class TaskPayload(
val initiator: String
) : EventPayload()
The error
Class 'TaskPayload' is not registered for polymorphic serialization in the scope of 'EventPayload'.
Mark the base class as 'sealed' or register the serializer explicitly.
kotlinx.serialization.SerializationException: Class 'TaskPayload' is not registered for polymorphic serialization in the scope of 'EventPayload'.
Mark the base class as 'sealed' or register the serializer explicitly.

Related

Is it considered a good practice using classes that extend an abstract class as #RequestBody?

I´m working on a legacy spring boot project that makes a strong reuse of a DTO class in a generic controller and in multiple services:
#PostMapping
controller.input(#RequestBody MyTypeDto type) { ... }
service.resolve(MyTypeDto type) { ... }
processor.send(MyTypeDto type) { ... }
I want to start decoupling it by creating another endpoint and making MyTypeDto an abstract class.
My biggest concern under all is the compatility with jackson.
public abstract class MyTypeDto { ... }
public class AnotherTypeDto extends MyTypeDto { ... }
public class AndAnotherTypeDto extends MyTypeDto { ... }
Is it considered a good practice?
As it is implied on your question, you controller endpoint is generic, it takes the input, creates the type, pass it to service based on subtype. Otherwise, you will end up many endpoints which all doing is creating the subtype and pass it to service.
If Jackson is your concern, Jackson has mechanism for subtypes. Please note you have to send one additional field which act as the discriminator (in this example, it is called type to decide which sub type to create.
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes({#JsonSubTypes.Type(value = FirstSubDto.class, name = "First"),
#JsonSubTypes.Type(value = SecondSubDto.class, name = "Second")})
public abstract class MyTypeDto {
..
}

Impact of #JsonTypeInfo in REST endpoints when concrete implementations are used

I'm wondering about the effects of adding a #JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class") to an interface.
My use case is that I have an interface Message with many subtypes. I want to be able to deserialize and serialize lists of messages in one endpoint.
My classes:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
interface Message: Serializable
data class Message1(
val name: String,
val age: Int
)
data class Message2(
val name: String,
val nickName: String
)
And the respective endpoint:
#RestController
class MessageController {
#GetMapping("/messages")
fun getEndpoints(): List<Message> {
return listOf(
Message1("Marco", 22),
Message2("Polo", "Poli")
)
}
}
So far so good - but now I want another endpoint that uses one of the explicit classes and I get a serialization error in my test that #class is missing - I don't want to send that when I'm using a concrete class anyhow.
#RestController
class MessageController {
#PostMapping("/add1")
fun add(#RequestBody content: Message1) {
// do something
}
}
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Missing type id when trying to resolve subtype of [simple type, class com.tractive.tap.message.request.RequestDataDTO]: missing type id property '#class'; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class com.tractive.tap.message.request.RequestDataDTO]: missing type id property '#class'
at [Source: (PushbackInputStream); line: 1, column: 51]
Why is the #class expected even though I'm using a concrete class? Is this expected behavior or am I doing something wrong?
Well, it is expected because by annotating the class with #JsonTypeInfo - via class-interface inheritance - you have explicitly instructed your Jackson to expect this information.
#JsonTypeInfo accepts parameter defaultImpl of type Class<?> that will be used
if type identifier is either not present, or can not be mapped to a registered type
You could use that to default your deserialisation to one type of message, preferably the most widely used explicitly in your api. For other types, you would still have to include the class info for Jackson.

Copy properties from source to target class using beanutils.properties method

I have a source class Entity having some params and a target class Dto class.
Now, Entity class is having params of some other class but in dto class I'm using those params directly not using the other class reference in dto.
Problem is : while doing BeanUtils.copyProperties(source,target) those properties having reference to other class does not get copies to dto class.
Entity class:
Public class Entity{
private A a;
private String add;
}
Public class A{
private String name;
}
Dto class :
public class Dto{
private String add;
private String name; // here instead of class A i directly took the param of class A as per requirement.
}
How can I do BeanUtils.copyProperties(Entity,Dto); so that all properties get copied.
The entity class contains params from other entity class but dto does not contain the reference to other class instead directly having other class params.
Spring has the concept of Converters that are used automatically when it has to convert between classes. BeanUtils.copyProperties doesn't seem to use it, but it's should be difficult using BeanWrapper to write an alternative that loops over properties and attempts to use Converters (in this case a Converter from A to String) to copy the properties.
Alternatively there are other libraries that can do similar tasks: Dozer, ModelMapper, MapStruct just to name a few without any preference.

sealed class vs enum when using associated type

I'd like to create a color object based on an Int. I can achieve the same result using sealed class and enum and was wondering if one is better than the other.
Using sealed class:
sealed class SealedColor(val value: Int) {
class Red : SealedColor(0)
class Green : SealedColor(1)
class Blue : SealedColor(2)
companion object {
val map = hashMapOf(
0 to Red(),
1 to Green(),
2 to Blue()
)
}
}
val sealedColor: SealedColor = SealedColor.map[0]!!
when (sealedColor) {
is SealedColor.Red -> print("Red value ${sealedColor.value}")
is SealedColor.Green -> print("Green value ${sealedColor.value}")
is SealedColor.Blue -> print("Blue value ${sealedColor.value}")
}
Using enum:
enum class EnumColor(val value: Int) {
Red(0),
Green(1),
Blue(2);
companion object {
fun valueOf(value: Int): EnumColor {
return EnumColor
.values()
.firstOrNull { it.value == value }
?: throw NotFoundException("Could not find EnumColor with value: $value")
}
}
}
val enumColor: EnumColor = EnumColor.valueOf(0)
when (enumColor) {
EnumColor.Red -> print("Red value ${enumColor.value}")
EnumColor.Green -> print("Green value ${enumColor.value}")
EnumColor.Blue -> print("Blue value ${enumColor.value}")
}
Are they equivalent in term of performance? Is there a better kotlin way to achieve the same result?
Let's discuss the difference between enums and sealed classes over various aspects with contrasting examples. This will help you choose one over the other depending on your use case.
Properties
Enum
In enum classes, each enum value cannot have its own unique property. You are forced to have the same property for each enum value:
enum class DeliveryStatus(val trackingId: String?) {
PREPARING(null),
DISPATCHED("27211"),
DELIVERED("27211"),
}
Here we need the trackingId only for the DISPATCHED and DELIVERED, the PREPARING is forced to have a null value.
Sealed Class
In case of sealed classes, we can have different properties for each subtype:
sealed class DeliveryStatus
class Preparing() : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
class Delivered(val trackingId: String, val receiversName: String) : DeliveryStatus()
Here we have different properties for each subtype. Preparing doesn't need properties for our use case, so we have the flexibility to not specify any property unlike forced null values in enums. Dispatched has one property while the Delivered has two properties.
Considering the example Color(val value: Int) in the question, you have a common value: Int property for all constants and since you don't need different properties for different constants, you should use enums in this case.
Functions
Enum
Enums can have abstract functions as well as regular functions. But like properties, each enum value also has to have the same function:
enum class DeliveryStatus {
PREPARING {
override fun cancelOrder() = println("Cancelled successfully")
},
DISPATCHED {
override fun cancelOrder() = println("Delivery rejected")
},
DELIVERED {
override fun cancelOrder() = println("Return initiated")
};
abstract fun cancelOrder()
}
In this example, we have an abstract function cancelOrder() that we have to override in each enum value. That means, we can't have different functions for different enum values.
Usage:
class DeliveryManager {
fun cancelOrder(status: DeliveryStatus) {
status.cancelOrder()
}
}
Sealed Class
In sealed classes we can have different functions for different subtypes:
sealed class DeliveryStatus
class Preparing : DeliveryStatus() {
fun cancelOrder() = println("Cancelled successfully")
}
class Dispatched : DeliveryStatus() {
fun rejectDelivery() = println("Delivery rejected")
}
class Delivered : DeliveryStatus() {
fun returnItem() = println("Return initiated")
}
Here we have different functions: cancelOrder() for Preparing, rejectDelivery() for Dispatched and returnItem() for Delivered. This makes the intent clearer and makes the code more readable, also we have the option of not having the function, in case we don't want to.
Usage:
class DeliveryManager {
fun cancelOrder(status: DeliveryStatus) = when(status) {
is Preparing -> status.cancelOrder()
is Dispatched -> status.rejectDelivery()
is Delivered -> status.returnItem()
}
}
If we want a common function for all subtypes like in the enum example, we can have that in sealed class by defining it in the sealed class itself and then overriding it in the subtypes:
sealed class DeliveryStatus {
abstract fun cancelOrder()
}
The advantage of having a common function for all types is that we don't have to type check using the is operator. We can simply use polymorphism as shown in the DeliveryManager class of enum example.
Inheritance
Enum
Since enum values are objects, they cannot be extended:
class LocallyDispatched : DeliveryStatus.DISPATCHED { } // Error
The enum class is implicitly final, so it cannot be extended by other classes:
class FoodDeliveryStatus : DeliveryStatus() { } // Error
Enum classes cannot extend other classes, they can only extend interfaces:
open class OrderStatus { }
interface Cancellable { }
enum class DeliveryStatus : OrderStatus() { } // Error
enum class DeliveryStatus : Cancellable { } // OK
Sealed Class
Since sealed class' subtypes are types, they can be extended:
class LocallyDispatched : Dispatched() { } // OK
The sealed class itself can be extended, of course!:
class PaymentReceived : DeliveryStatus() // OK
Sealed classes can extend other classes as well as interfaces:
open class OrderStatus { }
interface Cancellable { }
sealed class DeliveryStatus : OrderStatus() { } // OK
sealed class DeliveryStatus : Cancellable { } // OK
Number of Instances
Enum
Since enum values are objects and not types, we cannot create multiple instances of them:
enum class DeliveryStatus(val trackingId: String?) {
PREPARING(null),
DISPATCHED("27211"),
DELIVERED("27211"),
}
In this example, DISPATCHED is an object and not a type, so it can exist only as a single instance, we can't create more instances from it:
// Single instance
val dispatched1 = DeliveryStatus.DISPATCHED // OK
// Another instance
val dispatched2 = DeliveryStatus.DISPATCHED("45234") // Error
Sealed Class
Subtypes of sealed classes are types, so we can create multiple instances of these types. We can also make a type to have only a single instance using an object declaration:
sealed class DeliveryStatus
object Preparing : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
data class Delivered(val receiversName: String) : DeliveryStatus()
In this example, we can create multiple instances of Dispatched and Delivered. Notice that we have utilised the ability of the subtypes of the sealed class to be as a singleton object, a regular class or a data class. Preparing can can have only a single object, just like an enum value:
// Multiple Instances
val dispatched1 = Dispatched("27211") // OK
val dispatched2 = Dispatched("45234") // OK
// Single Instance
val preparing1 = Preparing // OK
val preparing2 = Preparing() // Error
Notice also that in the code above, each instance of Dispatched can have a different value for the trackingId property.
Serializable and Comparable
Enum
Every enum class in Kotlin is implicitly extended by the abstract class java.lang.Enum. So, all the enum values automatically have the implementations for equals(), toString(), hashCode(), Serializable and Comparable. We don't have to define them.
Sealed Class
For sealed classes we need to define them manually or use data class for the automatic equals(), toString() and hashcode() and then implement Serializable and Comparable manually.
Performance
Enum
Enums don't get garbage collected, they stay in memory for the lifespan of your app. This can be an upside or a downside.
Garbage collection process is expensive. The same is true for object creation, we don't want to create the same objects again and again. So, with enums, you save the cost of garbage collection as well as object creation. This is the upside.
The downside is that the enums stay in memory even when they are not in use, this can keep the memory occupied all the time.
You don't need to worry about all this, if you have 100 to 200 enums in your app. But when you have more than that, you have a decision to make whether you should go for enums depending on the facts such as the number of enums, whether they will be in use all the time and the amount of memory allocated to your JVM.
The comparison of enum values is faster in the when expression because under the hood, it uses tableswitch to compare the objects. So, for the example given in the question, enums should be preferred as they will be faster in this case.
In Android, when the optimization is enabled, the Proguard converts the enums that don't have functions and properties to integers, so you get the type-safety of the enums at compile-time and the performance of the ints at runtime!
Sealed Class
Sealed classes are just regular classes with the only exception that they need to be extended in the same package and the same compilation unit. So, their performance is equivalent to regular classes.
Objects of the subtypes of the sealed classes get garbage collected like the objects of regular classes. So, you have to bear the cost of garbage collection as well as object creation.
When you have the low memory constraints, you may consider using sealed classes instead of enums, if you need thousands of objects. Because the garbage collector can free up the memory when the objects are not in use.
If you use object declaration for extending the sealed class, the objects act as singletons and they won't be garbage collected, just like enums.
The comparison of sealed class' types is slower in when expression because under the hood it uses instanceof to compare the types. The speed difference between enums and sealed classes, in this case, is very little though. It matters only when you are comparing thousands of constants in a loop.
That's it! Hopefully, this will make it easier for you to choose one over the other.
A sealed class is "an extension of enum classes". They can exist in multiple instances that contain state while each enum constant exists only as a single instance.
Since, in your example, you don't need the values to be instantiated multiple times and they don't provide special behavior, enums should just be right for the use case.
Also, see the docs.
My solution is like this and it will give you list of all subclass instance val list = BaseClass::class.sealedSubclasses.map{it.createInstance()}

asp.net web api wrong serialization output

I inherited a model written in VB.NET that I'm trying to expose as a REST service. I have a class that inherits from a base abstract class. When I try to return a collection of the concrete class, what I get is an empty json representation like this:
[{},{},{},{},{},{},{}]
Why is this? Is it because of abstract class? I'm using MVC 4 RC. My code:
Abstract class:
<Serializable()> _
Partial Public MustInherit Class Topic
Public Property Topic_Key() As Integer
Get
Return m_Topic_Key
End Get
Set(ByVal value As Integer)
m_Topic_Key = value
End Set
End Property
End Class
Concrete class:
<Serializable()> _
Public Class ProductPortfolio
Inherits Topic
End Class
Api Controller:
public class PortfoliosController : ApiController
{
public List<ProductPortfolio> Get()
{
return ProductPortfolio.GetAll().ToList();
}
}
Put this on your abstract class
<System.Runtime.Serialization.KnownType(GetType(ProductPortfolio))>

Resources