-
Notifications
You must be signed in to change notification settings - Fork 637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optional properties are not working on Kotlin 1.5.31 #1705
Comments
Can you please show how you apply this serializer? |
class OptionalPropertySerializer<T>(
private val valueSerializer: KSerializer<T>
) : KSerializer<Optional<T>> {
final override val descriptor: SerialDescriptor = valueSerializer.descriptor
final override fun deserialize(decoder: Decoder): Optional<T> {
return try {
Optional.Value(valueSerializer.deserialize(decoder))
} catch (exception: Exception) {
L.e(exception)
Optional.NotPresent
}
}
final override fun serialize(encoder: Encoder, value: Optional<T>) {
val msg = "Tried to serialize an optional property that had no value present. Is encodeDefaults false?"
when (value) {
Optional.NotPresent -> throw SerializationException(msg)
is Optional.Value ->
when (val optional = value.value) {
null -> encoder.encodeNull()
else -> valueSerializer.serialize(encoder, optional)
}
}
}
}
@Serializable(OptionalPropertySerializer::class)
sealed class Optional<out T : Any?> {
object NotPresent : Optional<Nothing>()
data class Value<T : Any?>(val value: T?) : Optional<T>()
fun get(): T? {
return when (this) {
NotPresent -> null
is Value -> this.value
}
}
} And then in data classes:
I believe it's identical as in linked blog post. Also I had breakpoint on line: |
As a workaround, you need to remove the |
Still happens in 1.6.10 and 1.6.0 and is preventing us from upgrading |
That's not a feasible workaround, that class is used a couple of thousand times in our project |
Tools -> Show Kotlin BytecodeI just checked the generated byte code from the compiler plugin, and it's completely identicalEdit 1: If you look at the serializer for this class @Serializable
data class Test(val optional: Optional<String?> = Optional.Missing()) It gives you this snippet @NotNull
public KSerializer[] childSerializers() {
return new KSerializer[]{new OptionalSerializer(BuiltinSerializersKt.getNullable((KSerializer)StringSerializer.INSTANCE))};
} But if you invoke (Test.serializer() as GeneratedSerializer).childSerializers() the dataSerializer becomes UnitSerializer Edit 2: BuiltinSerializersKt.getNullable((KSerializer)StringSerializer.INSTANCE) Through the debugger you get a String? serializer Detailed explanation why it's brokenEdit 3: Now decompiling the output inside the buildfolder I found this lines @NotNull
public KSerializer[] childSerializers() {
KSerializer[] var1 = new KSerializer[]{Optional.Companion.serializer((KSerializer)UnitSerializer.INSTANCE), (KSerializer)IntSerializer.INSTANCE};
return var1;
}
// Snippets from deserialize method
var6 = var8.decodeSerializableElement(var2, 0, (DeserializationStrategy)Optional.Companion.serializer((KSerializer)UnitSerializer.INSTANCE), var6);
var6 = var8.decodeSerializableElement(var2, 0, (DeserializationStrategy)Optional.Companion.serializer((KSerializer)UnitSerializer.INSTANCE), var6); (Full class: https://haste.schlaubi.me/xojosijopa.avrasm) So it looks like the compiler plugin actually screws up here Edit 4: It seems like this is the causing commit JetBrains/kotlin@3020e9c#diff-ca6fbd7989b6eb22df5d83397fd7bf6eb71ae4fbfb6dda8d260b14613a4f96fb val serializer = findStandardKotlinTypeSerializer(baseClass.module, context.irBuiltIns.unitType.toKotlinType())!! Passing in Edit 5: the line mentioned above has this comment
Whilst this might be true about the auto generated serializers for sealed classes, it is definitivly not for the Optional serializer here, as it is custom and in fact makes use of the type used, therefore you can't just pass in Unit My suggestion would be, to disable this kind of optimisation if the serializer is custom, although I don't know how to do that, as I am not that far into the compiler codebase I figured out a new workaround, as the issue explained above only affects abstract and sealed classes, i just made my optional class open and made the constructor private to get the same "sealed" effect This obviously misses out on the sealed advantages, like when statements recognizing all subclasses is calls as exhaustive, but it is a way easier workaround then what @shanshin suggested |
Related YouTrack ticket: https://youtrack.jetbrains.com/issue/KT-50764 |
For fix of issues #1705 KT-50764 KT-50718
#KT-50764 Fixed #KT-50718 Fixed Fixes Kotlin/kotlinx.serialization#1705
#KT-50764 Fixed #KT-50718 Fixed Fixes Kotlin/kotlinx.serialization#1705 (cherry picked from commit aa1700f)
#KT-50764 Fixed #KT-50718 Fixed Fixes Kotlin/kotlinx.serialization#1705 (cherry picked from commit aa1700f)
#KT-50764 Fixed #KT-50718 Fixed Fixes Kotlin/kotlinx.serialization#1705 (cherry picked from commit aa1700f)
#KT-50764 Fixed #KT-50718 Fixed Fixes Kotlin/kotlinx.serialization#1705 (cherry picked from commit aa1700f)
For fix of issues #1705 KT-50764 KT-50718
For fix of issues #1705 KT-50764 KT-50718
I'm dealing with demanding API, that has 3 states: value, null value, no value. It's a case described in blog post here: https://medium.com/livefront/kotlinx-serialization-de-serializing-jsons-nullable-optional-properties-442c7f0c2614
To Reproduce
I have serialiser as in blog post:
on Kotlin 1.5.30 this
valueSerializer
was injected correctly. However on 1.5.31 I'm always getting there instance of:kotlinx.serialization.internal.UnitSerializer
Expected behavior
It should work as in Kotlin 1.5.30. I'm not sure what created that regression (https://github.com/JetBrains/kotlin/releases/tag/v1.5.31) or is it serialisation issue or Kotlin's.
Environment
The text was updated successfully, but these errors were encountered: