Amaan
02/23/2022, 4:05 PMjava.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
. Here is my attached code, but essentially I want a single function that can publish all my events and every time I want to create a new type of event, I can just create a class that extends it with the right payload type.Amaan
02/23/2022, 4:05 PMimport io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import <http://io.ktor.client.request.post|io.ktor.client.request.post>
import io.ktor.client.request.setBody
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
sealed class Event<T>(
val message: String, val payload: T
) {
@Serializable
data class UserCreated(
val user: User
) : Event<User>(
"User created", user
)
}
@Serializable
data class User(
val id: String,
val userName: String
)
object EventPublisher {
// private val client = HttpClient(CIO)
suspend fun <T> publish(event: Event<T>) {
println(Json.encodeToString(event)) // Represents the client call below which also doesn't work
// <http://client.post|client.post>("<http://127.0.0.1:8080/events>") {
// setBody(event)
// }
}
}
suspend fun main() {
val user = User("id", "username")
EventPublisher.publish(Event.UserCreated(user))
}
Amaan
02/23/2022, 4:06 PMimplementation(platform("io.ktor:ktor-bom:2.0.0-beta-1"))
implementation("io.ktor:ktor-client-core")
implementation("io.ktor:ktor-client-content-negotiation")
implementation("io.ktor:ktor-client-serialization")
implementation("io.ktor:ktor-client-cio")
with the kotlinx-serialization pluginNikky
02/23/2022, 9:36 PMAmaan
02/23/2022, 9:36 PMNikky
02/23/2022, 9:39 PMimport kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.serializer
@Serializable
open class Box<T>(val contents: T)
@Serializable
data class StringBox<T>(
private val data: T
) : Box<T>(data) where T: String
fun main() {
println(
Json.encodeToString(
// StringBox.serializer(String.serializer()),
StringBox("Kotlin")
)
)
}
the trick is that it seems to only be able to use generics when passed all the way up..Nikky
02/23/2022, 9:40 PMAmaan
02/23/2022, 9:42 PMNikky
02/23/2022, 10:10 PM@Serializable
data class UserCreated<USER>(
val user: USER
) : Event<USER>(
"User created", user
) where USER : User
and kotlinx-serialization should deal with it properlyNikky
02/23/2022, 10:11 PMNikky
02/23/2022, 10:18 PMAmaan
02/23/2022, 10:24 PM@Serializable
open class Event<T>(val message: String, val contents: T)
@Serializable
class UserCreatedEvent<T>(
private val user: T
) : Event<T>("StringEvent", user) where T: User
@Serializable
class UserUpdatedEvent<T>(
private val oldUser: T,
private val newUser: T
) : Event<Diff<T>>("StringEvent", Diff(oldUser, newUser)) where T: User
@Serializable
data class Diff<T>(
val old: T,
val new: T
)
@Serializable
data class User(
val id: String,
val name: String
)
Amaan
02/23/2022, 10:25 PMfun main() {
val user = User("id", "Alice")
println(Json.encodeToString(UserCreatedEvent(user)))
val updatedUser = user.copy(name = "Bob")
println(Json.encodeToString(UserUpdatedEvent(user, updatedUser)))
}
but not this
fun main() {
val user = User("id", "Alice")
println(EventPublisher.publish(UserCreatedEvent(user)))
val updatedUser = user.copy(name = "Bob")
println(EventPublisher.publish(UserUpdatedEvent(user, updatedUser)))
}
object EventPublisher {
// private val client = HttpClient(CIO)
fun <T> publish(event: Event<T>) {
println(Json.encodeToString(event)) // Represents the client call below which also doesn't work
// <http://client.post|client.post>("<http://127.0.0.1:8080/events>") {
// setBody(event)
// }
}
}
Nikky
02/23/2022, 10:25 PM@Serializable
sealed class Event<T>(
val message: String,
) {
abstract val payload: T
@Serializable
data class UserCreated(
val user: User,
) : Event<User>(
"User created"
) {
override val payload = user
}
}
Nikky
02/23/2022, 10:25 PMAmaan
02/23/2022, 10:29 PMEventPublisher.publish
functionNikky
02/23/2022, 10:36 PMNikky
02/23/2022, 10:37 PMobject EventPublisher {
// private val client = HttpClient(CIO)
@OptIn(InternalSerializationApi::class)
suspend inline fun <reified T> publish(event: Event<T>, json: Json = Json.Default) {
println(Json.encodeToString(
event
))
// Represents the client call below which also doesn't work
// <http://client.post|client.post>("<http://127.0.0.1:8080/events>") {
// setBody(event)
// }
}
}
Nikky
02/23/2022, 10:44 PMAmaan
02/23/2022, 10:47 PMkotlinx.serialization.SerializationException: Class 'User' is not registered for polymorphic serialization in the scope of 'User'.
with the change you suggestedAmaan
02/23/2022, 10:51 PMEvent
class is sealed, I get that SerializationException
, but when it's open, I don't. I wonder if that's a separate bug that needs to be reported.Nikky
02/23/2022, 10:52 PMimport kotlinx.serialization.*
import kotlinx.serialization.json.Json
@Serializable
abstract class Event<T>(
val message: String,
) {
abstract val contents: T
}
@Serializable
class UserCreatedEvent(
private val user: User
) : Event<User>("StringEvent") {
override val contents = user
}
@Serializable
class UserUpdatedEvent(
private val oldUser: User,
private val newUser: User
) : Event<Diff<User>>("StringEvent") {
override val contents = Diff(oldUser, newUser)
}
@Serializable
data class Diff<T>(
val old: T,
val new: T
)
@Serializable
data class User(
val id: String,
val name: String
)
object EventPublisher {
@OptIn(InternalSerializationApi::class)
inline fun <reified EVENT: Event<T>, T> publish(event: EVENT, serializer: KSerializer<EVENT>, json: Json = Json.Default): String {
return json.encodeToString(
serializer,
event
)
}
}
suspend fun main() {
val user = User("id", "Alice")
println(EventPublisher.publish(UserCreatedEvent(user), UserCreatedEvent.serializer()))
val updatedUser = user.copy(name = "Bob")
println(EventPublisher.publish(UserUpdatedEvent(user, updatedUser), UserUpdatedEvent.serializer()))
}
Nikky
02/23/2022, 10:52 PMAmaan
02/23/2022, 10:54 PMwhere
, inline
, and reified
keywords better. I'll try this on the actual code I was writing which has a few more layers of indirection and get back to you if it doesn't work.Nikky
02/23/2022, 10:58 PMNikky
02/23/2022, 10:58 PMNikky
02/23/2022, 10:58 PM