-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
KTOR-7620 Make Url class @Serializable and JVM Serializable #4421
base: 3.1.0-eap
Are you sure you want to change the base?
Conversation
7c7b289
to
62e380e
Compare
@Suppress("UNCHECKED_CAST") | ||
public actual fun <T : Any> JvmSerializerReplacement(serializer: JvmSerializer<T>, value: T): Any = | ||
DefaultJvmSerializerReplacement(serializer, value) | ||
|
||
@PublishedApi // IMPORTANT: changing the class name would result in serialization incompatibility | ||
internal class DefaultJvmSerializerReplacement<T : Any>( | ||
private var serializer: JvmSerializer<T>?, | ||
private var value: T? | ||
) : Externalizable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should such an API better be integrated as part of the Kotlin stdlib itself or at least in a separate library for reuse in other projects? It could also be useful in kotlinx-datetime: Kotlin/kotlinx-datetime#373
And I'll also add the same code to my open-source lib, too (https://github.com/ensody/ReactiveState-Kotlin).
That's unnecessary duplication in multiple places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding JvmSreializable
interface, you can vote on this issue:
- KT-48243 Publish
kotlin.io.Serializable
I haven't found any issue proposing multiplatform Externalizable
implementation, so feel free to create one!
62e380e
to
8db09e6
Compare
} | ||
} | ||
|
||
internal object UrlJvmSerializer : JvmSerializer<Url> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an idea: To make this slightly easier to implement one could also add a reusable serializer which utilizes kotlinx-serialization (e.g. using JSON or BSON or CBOR internally).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea! Would you like to implement it in this PR or in a separate one?
8db09e6
to
d217ee0
Compare
a65ff10
to
9ae3d49
Compare
92cfa95
to
6666ecf
Compare
In our project we had to define our own UrlSerializer. It would be much nicer to have this in the Ktor library itself, so it works out of the box (similar to how Cookie was recently extended). Also, types like Url and Cookie should be java.io.Serializable. Otherwise Android crashes when using those types as e.g. screen arguments. This happens very quickly when Url is used indirectly as part of a data class where we wanted type safety.
6666ecf
to
9057d9f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you tell me why can't we mark it with @serializable from kotlinx.serialization?
This PR is doing that, but that's not enough. On Android many APIs work with anything that's Parcelable/Serializable. Imagine you have some random data class which contains a It's very error-prone and inconvenient to force everyone to check all the attributes of every data class and manually use kotlinx.serialization in many different places to ensure that the data is compatible with the Android API. The better solution is to make all widely used types compatible with For this to be possible, the types that are very commonly used in the UI like |
That sounds good; let's mark parent interfaces with the @osipxd, could you also check? |
@@ -254,3 +262,22 @@ internal val Url.encodedUserAndPassword: String | |||
get() = buildString { | |||
appendUserAndPassword(encodedUser, encodedPassword) | |||
} | |||
|
|||
public class UrlSerializer : KSerializer<Url> { | |||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Url", PrimitiveKind.STRING) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use a qualified name
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Url", PrimitiveKind.STRING) | |
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("io.ktor.http.Url", PrimitiveKind.STRING) |
} | ||
} | ||
|
||
internal object UrlJvmSerializer : JvmSerializer<Url> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea! Would you like to implement it in this PR or in a separate one?
@Suppress("UNCHECKED_CAST") | ||
public actual fun <T : Any> JvmSerializerReplacement(serializer: JvmSerializer<T>, value: T): Any = | ||
DefaultJvmSerializerReplacement(serializer, value) | ||
|
||
@PublishedApi // IMPORTANT: changing the class name would result in serialization incompatibility | ||
internal class DefaultJvmSerializerReplacement<T : Any>( | ||
private var serializer: JvmSerializer<T>?, | ||
private var value: T? | ||
) : Externalizable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding JvmSreializable
interface, you can vote on this issue:
- KT-48243 Publish
kotlin.io.Serializable
I haven't found any issue proposing multiplatform Externalizable
implementation, so feel free to create one!
val encoded = ByteArrayOutputStream().also { | ||
ObjectOutputStream(it).writeObject(obj) | ||
}.toByteArray() | ||
return ObjectInputStream(encoded.inputStream()).readObject() as T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the assertEquals
call be right here?
return ObjectInputStream(encoded.inputStream()).readObject() as T | |
val decoded = ObjectInputStream(encoded.inputStream()).readObject() as T | |
assertEquals(obj, decoded) | |
return decoded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would have to be optional (default true) because not all objects might be equal after deserialization, though in most cases they would be. But yes, that's actually how we do in our implementation. I was just afraid of the code looking 100% the same...
@@ -254,3 +262,22 @@ internal val Url.encodedUserAndPassword: String | |||
get() = buildString { | |||
appendUserAndPassword(encodedUser, encodedPassword) | |||
} | |||
|
|||
public class UrlSerializer : KSerializer<Url> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be an object. And probably make it inner object Url.Serializer
public class UrlSerializer : KSerializer<Url> { | |
public object UrlSerializer : KSerializer<Url> { |
In our project we had to define our own UrlSerializer. It would be much nicer to have this in the Ktor library itself, so it works out of the box (similar to how Cookie was recently extended).
Also, types like Url and Cookie should be java.io.Serializable. Otherwise Android crashes when using those types as e.g. screen arguments. This happens very quickly when Url is used indirectly as part of a data class where we wanted type safety.