Kotlin / kotlinx.serialization

Kotlin multiplatform / multi-format serialization
Apache License 2.0
5.41k stars 620 forks source link

Library of JDK type serializers? #1931

Open mikehearn opened 2 years ago

mikehearn commented 2 years ago

Are there any libraries, community or otherwise, that add serializers for common JDK types like Date, Instant, PublicKey, URI, URL etc? It feels like one should exist by now but there's no mention of such an effort in the docs as far as I can tell.

pdvrieze commented 2 years ago

@mikehearn I'd be happy to contribute to one. I might even be interested in seeing how such a library could be made to "autoregister"

sandwwraith commented 2 years ago

There isn't one that I'm aware of. We had such plans in the past, but we never implemented it, see the rationale here: https://github.com/Kotlin/kotlinx.serialization/pull/350#discussion_r251471644

mikehearn commented 2 years ago

The rationale being that people might want features it doesn't have yet?

pdvrieze commented 2 years ago

@mikehearn another issue is that when interacting with external systems (rather than just writing reading its own data) there are different ways to serialize the same type. Especially dates are an issue for that, but other types too.

qwwdfsad commented 2 years ago

I'd be happy to contribute to one

We would be happy to accept your contribution! The main rationale behind #350 not getting merged is our lack of time along with pretty minor design questions (e.g. what should and shouldn't be serialized, what serialization form should be used for controversial java.util.Date and so on). To avoid redundant work, we can start from just a checklist of what you are interested in contributing with a small description of serialized form in a draft PR, if that works for you.

I might even be interested in seeing how such a library could be made to "autoregister"

We have some very vague plans regarding this that are blocked by our current lack of "resource" concept in klibs. The basic idea is to ship static list of serializers, call it "module" and teach serializer plugin to lookup them.

aSemy commented 2 years ago

I'm interested in a common library that would support BigDecimal and BigInteger (when #2041 is released).

I think it would really help with using KxS, as the learning curve is very steep at the beginning. Serialising any non-standard type requires quite a lot of investment to understand how to implement custom serialisers.

The point raised in https://github.com/Kotlin/kotlinx.serialization/pull/350#discussion_r251471644 (how should java.io.File be handled?) could be handled by using the recently added typealias serializaton method

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import java.io.File

/** Encodes a File as a string, using [invariantSeparatorsPath] */
typealias FileAsInvariantPath = @Serializable(with = FileAsInvariantStringSerializer::class) File

object FileAsInvariantStringSerializer : KSerializer<File> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.io.File", PrimitiveKind.STRING)

    override fun deserialize(decoder: Decoder): File = File(decoder.decodeString())

    override fun serialize(encoder: Encoder, value: File) = encoder.encodeString(value.invariantSeparatorsPath)
}

This means the serialisation behaviour is explicit and clear, while still being seamless with any existing code. It also gives some good examples that users can copy and adapt to implement their own serializers.

Kantis commented 2 years ago

Would love to work with you on that @aSemy. I threw up a repo here

sschuberth commented 1 year ago

FYI, for Java classes that be constructed from a string, I came up with the rather general approach

/**
 * A convenience function for creating a [ToStringSerializer] whose name is derived from the class name.
 */
inline fun <reified T : Any> toStringSerializer(noinline create: (String) -> T): ToStringSerializer<T> =
    ToStringSerializer(T::class.java.name, create)

/**
 * A serializer with the given serial name that uses [Any] instance's [toString] function for serialization and the
 * given [create] function for deserialization.
 */
class ToStringSerializer<T : Any>(serialName: String, private val create: (String) -> T) : KSerializer<T> {
    override val descriptor = PrimitiveSerialDescriptor(serialName, PrimitiveKind.STRING)
    override fun serialize(encoder: Encoder, value: T) = encoder.encodeString(value.toString())
    override fun deserialize(decoder: Decoder) = create(decoder.decodeString())
}

which can be used like

/**
 * A (de-)serializer for Java [File] instances from / to strings.
 */
object FileSerializer : KSerializer<File> by toStringSerializer(::File)

or

/**
 * A (de-)serializer for Java [URI] instances class from / to strings.
 */
object URISerializer : KSerializer<URI> by toStringSerializer(::URI)