spierce7
08/17/2023, 9:37 PMoneof
protobufs to me that I need to deserialize:
message DeviceConnected {
string serialNumber = 1;
int32 vendorId = 2;
int32 productId = 3;
}
message DataFromDevice {
string serialNumber = 1;
bytes data = 2;
}
message DataToDeviceResult {
string correlationId = 1;
bool success = 2;
}
message ServerMessage {
oneof message {
DeviceConnected deviceConnected = 1;
DataFromDevice fromDevice = 2;
DataToDeviceResult toDeviceResult = 3;
}
}
How would I represent these in Kotlinx.Serialization? The attempts I've tried have resulted in serialization errors, and I really am not sure how to represent this polymorphic proto.Dariusz Kuc
08/17/2023, 10:00 PMspierce7
08/17/2023, 10:55 PMimport kotlinx.serialization.Polymorphic
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
import kotlinx.serialization.protobuf.ProtoBuf
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class ExampleTest {
@Test
fun testSealedInterface() { // Put this function in a class in test sources.
val module = SerializersModule {
polymorphic(ServerMessage::class) {
subclass(ServerMessage.DeviceConnected::class)
subclass(ServerMessage.DataFromDevice::class)
subclass(ServerMessage.DataToDeviceResult::class)
}
}
val protobuf = ProtoBuf { serializersModule = module }
ServerMessage.DeviceConnected("test", 1, 2).let { initial ->
val byteArray = protobuf.encodeToByteArray(initial)
val deserialized = protobuf.decodeFromByteArray<ServerMessage>(byteArray)
assertTrue(deserialized is ServerMessage.DeviceConnected)
assertEquals("test", deserialized.serialNumber)
}
}
}
@Serializable
@Polymorphic
sealed interface ServerMessage {
@Serializable
data class DeviceConnected(
val serialNumber: String,
val vendorId: Int,
val productId: Int
) : ServerMessage
@Serializable
data class DataFromDevice(
val serialNumber: String,
val data: ByteArray
) : ServerMessage
@Serializable
data class DataToDeviceResult(
val correlationId: String,
val success: Boolean
) : ServerMessage
}
Yields:
kotlinx.serialization.SerializationException: Class 'test' is not registered for polymorphic serialization in the scope of 'ServerMessage'.
To be registered automatically, class 'test' has to be '@Serializable', and the base class 'ServerMessage' has to be sealed and '@Serializable'.
Alternatively, register the serializer for 'test' explicitly in a corresponding SerializersModule.
at kotlinx.serialization.internal.AbstractPolymorphicSerializerKt.throwSubtypeNotRegistered(AbstractPolymorphicSerializer.kt:102)
at kotlinx.serialization.PolymorphicSerializerKt.findPolymorphicSerializer(PolymorphicSerializer.kt:102)
at kotlinx.serialization.internal.AbstractPolymorphicSerializer.deserialize(AbstractPolymorphicSerializer.kt:56)
at kotlinx.serialization.protobuf.internal.ProtobufDecoder.decodeSerializableValue(ProtobufDecoding.kt:196)
at kotlinx.serialization.protobuf.internal.ProtobufDecoder.decodeSerializableValue(ProtobufDecoding.kt:186)
at kotlinx.serialization.protobuf.ProtoBuf.decodeFromByteArray(ProtoBuf.kt:137)
at ExampleTest.testSealedInterface(ExampleTest.kt:74)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:108)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
I'm not sure what's going on. There is no class test
glureau
08/18/2023, 8:33 AMglureau
08/18/2023, 8:36 AMand this is followed byCopy codeTestInt(id = "abc", 7).let<TestOneOf, _> { initial ->
protobuf.encodeToByteArray(initial)
.
But this method I believe is a reified one using the type of the parameter, so in the github issue it's equivalent to protobuf.encodeToByteArray<TestOneOf>(initial)
, aka the sealed class, and not one of its subclass. You may want to try to specify the generics in your code.glureau
08/18/2023, 8:42 AMChad Gregory
08/18/2023, 3:51 PMglureau
08/18/2023, 6:59 PMChad Gregory
08/18/2023, 7:06 PMplugins {
kotlin("plugin.serialization") version "1.8.22"
id("com.google.protobuf") version "0.9.4"
}
dependencies {
implementation("com.google.protobuf:protobuf-kotlin:3.24.0")
}
protobuf {
protoc {
// The artifact spec for the Protobuf Compiler
artifact = "com.google.protobuf:protoc:3.24.0"
}
generateProtoTasks {
all().forEach { task ->
task.plugins{
create("kotlin")
create("java")
}
}
}
}
Place your .proto file in app/source/main/proto
When you build your project it will generate the code to build/inject data based on that .proto filespierce7
08/19/2023, 7:50 PMglureau
08/21/2023, 7:16 AMChad Gregory
08/21/2023, 11:38 AM