smallufo
01/27/2023, 3:45 PMclass PageSerializer<T>(private val serializer: KSerializer<T>) : KSerializer<Page<T>> {
override val descriptor: SerialDescriptor
get() = buildClassSerialDescriptor("Page") {
element<Int>("totalPages")
element<Long>("totalElements")
element<List<T>>("contents")
}
override fun serialize(encoder: Encoder, value: Page<T>) {
encoder.encodeStructure(descriptor) {
encodeIntElement(descriptor, 0, value.totalPages)
encodeLongElement(descriptor, 1, value.totalElements)
encodeSerializableElement(descriptor, 2, ListSerializer(serializer), value.content)
}
}
override fun deserialize(decoder: Decoder): Page<T> {
return decoder.decodeStructure(descriptor) {
var totalPages: Int? = null
var totalElements: Long? = null
var contents: List<T> = emptyList()
loop@ while (true) {
when (val index = decodeElementIndex(descriptor)) {
DECODE_DONE -> break@loop
0 -> totalPages = decodeIntElement(descriptor, 0)
1 -> totalElements = decodeLongElement(descriptor, 1)
2 -> contents = decodeSerializableElement(descriptor, 2, ListSerializer(serializer))
else -> throw SerializationException("Unexpected index $index")
}
}
PageImpl(contents , Pageable.unpaged() , totalElements!!)
}
}
}
It seems ok , but I am not sure how to make use of it .
Say I have an wrapped Page<Session> object :
val pageSerializer: PageSerializer<Session> = PageSerializer(Session.serializer())
val sessionListFormat = Json {
serializersModule = SerializersModule {
polymorphic(Page::class) {
subclass(pageSerializer)
}
}
prettyPrint = true
}
sessionDao.findByUserOrderByCreatedDesc(user, Pageable.ofSize(10)).also { page: Page<Session> ->
<http://logger.info|logger.info> {
sessionListFormat.encodeToString(page)
}
}
And it throws :
java.lang.IllegalStateException: Captured type paramerer T from generic non-reified function. Such functionality cannot be supported as T is erased, either specify serializer explicitly or make calling function inline with reified T
How to correct make use of this serializer ? thank you.Adam S
01/27/2023, 4:37 PMsessionListFormat.encodeToString(page)
is the nice helper function, that will try to automatically get the serializer based on the generic type. If you use the explicit encodeToString()
function, you need to pass in the serializer as the first param:
sessionListFormat.encodeToString(pageSerializer, page)
that should worksmallufo
01/27/2023, 5:19 PMval pageSerializer: PageSerializer<Session> = PageSerializer(Session.serializer())
val sessionListFormat = Json {
serializersModule = SerializersModule {
}
prettyPrint = true
}
<http://logger.info|logger.info> { sessionListFormat.encodeToString(pageSerializer , page)
}
but it throws :
java.lang.IllegalStateException: Captured type paramerer T from generic non-reified function. Such functionality cannot be supported as T is erased, either specify serializer explicitly or make calling function inline with reified T
I also tried :
val sessionListFormat = Json {
serializersModule = SerializersModule {
polymorphic(Page::class) {
subclass(PageImpl::class , pageSerializer as KSerializer<PageImpl<*>>)
}
}
prettyPrint = true
}
but exception is the same Captured type paramerer T from generic non-reified function. Such functionality cannot be supported as T is erased, either specify serializer explicitly or make calling function inline with reified T
ephemient
01/27/2023, 6:27 PMsessionListFormat.encodeToString(pageSerializer, page)
ephemient
01/27/2023, 6:34 PM@Serializable
data class PageSurrogate<T>(
val totalPages: Int,
val totalElements: Long,
val contents: List<T>,
)
class PageSerializer<T>(tSerializer: KSerializer<T>) : KSerializer<Page<T>> {
private val surrogateSerializer = PageSurrogate.serializer(tSerializer)
override val descriptor: SerialDescriptor = SerialDescriptor("PageSerializer", surrogateSerializer.descriptor)
override fun serialize(encoder: Encoder, value: Page<T>) {
encoder.encodeSerializableValue(
surrogateSerializer,
PageSurrogate(
totalPages = page.totalPages,
totalElements = page.totalElements,
contents = page.contents,
)
)
}
override fun deserialize(decoder: Decoder): Page<T> {
val surrogate = decoder.decodeSerializableValue(surrogateSerializer)
return Page(
surrogate.totalPages,
surrogate.totalElements,
surrogate.contents,
)
}
}
smallufo
01/28/2023, 3:25 AMimport kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
@Serializable
data class PageSurrogate<T>(
val totalPages: Int,
val totalElements: Long,
val contents: List<T>
)
class PageSerializer<T>(tSerializer: KSerializer<T>) : KSerializer<Page<T>> {
private val surrogateSerializer = PageSurrogate.serializer(tSerializer)
@OptIn(ExperimentalSerializationApi::class)
override val descriptor: SerialDescriptor = SerialDescriptor("PageSerializer", surrogateSerializer.descriptor)
override fun serialize(encoder: Encoder, value: Page<T>) {
encoder.encodeSerializableValue(
surrogateSerializer,
PageSurrogate(
totalPages = value.totalPages,
totalElements = value.totalElements,
contents = value.content,
)
)
}
override fun deserialize(decoder: Decoder): Page<T> {
val surrogate = decoder.decodeSerializableValue(surrogateSerializer)
return PageImpl(surrogate.contents, Pageable.unpaged(), surrogate.totalElements)
}
}