kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

Global custom serializers

Open sksamuel opened this issue 5 years ago • 23 comments

What is your use-case and why do you need this feature?

If you have a data class with many "unsupported" types, but very common types in the JDK it is unweidly to use @ContextualSerialization everywhere - either on a file basic or a property basis.

For example,

data class Foo(val decimal: BigDecimal, val id: UUID, val date: LocalDateTime)

Three types, needing three @ContextualSerialization.

Describe the solution you'd like

Some way of registering global serializers.

sksamuel avatar Jul 04 '19 14:07 sksamuel

We have several concerns regarding this:

  1. Implicitness – it is very easy to forget and hard to find which serializer is used for UUID in a particular class. Easy to mess/mix things up.
  2. Security – you may accidentally deserialize data that you didn't want to in some area of the program
  3. Modules, incremental compilation – since serializers are compiled into the code by the plugin, changing global settings would require to recompile the whole module.

However, these problems may not concern small or mid-size applications. Probably this restriction would be relaxed in the future.

sandwwraith avatar Jul 11 '19 11:07 sandwwraith

Valid concerns, but here are a couple of points from the other side.

  1. Common types like BigDecimal, UUID, javax dates, etc are not supported at the moment. It's tedious and error prone to annotate every instance of these. These are extremely common base JDK types - in my code base for example a domain type could have 10 of these out of 30 fields, across hundreds of domain objects. Financial domain objects will use BigDecimals everywhere.

  2. The most popular serialisation frameworks - think Jackson for example - handle serialization of any types. Jackson will serialize everything. So there must be a way to make this work.

Edited: Removed GSON from 2.

sksamuel avatar Jul 11 '19 12:07 sksamuel

The Gson maintainers think point 2 is a failure of the library and have not supported this feature in Moshi (aka Gson v3). It has absolutely caused problems in practice.

JakeWharton avatar Jul 11 '19 12:07 JakeWharton

Ok I stand corrected on Gson. It's still a feature / bug (delete as appropriate) of Jackson though.

sksamuel avatar Jul 11 '19 12:07 sksamuel

I'm not sure whether it is actually valid (or easy to do), but package level annotations would be the way to go. I know it is possible in Java using package-info.java, but I've got no idea whether Kotlin could use such annotations in the compiler.

pdvrieze avatar Jul 16 '19 13:07 pdvrieze

AFAIR, Kotlin doesn't support package-level annotation. It recognizes annotations on package-info.java, but this requires enabling Java support in JVM target (hence, no MPP).

sandwwraith avatar Jul 17 '19 09:07 sandwwraith

@sandwwraith Was afraid that was the case. The only other option I can come up with would be some way to add annotations to an annotationB and have this annotation B be used as a shorthand wherever needed.

pdvrieze avatar Jul 17 '19 11:07 pdvrieze

Not global, but the file-level annotation @file:UseSerializers(MySerializer::class) mitigates the problem. See Specifying serializers for a file.

marco-eckstein avatar Feb 07 '21 15:02 marco-eckstein

Any update on this?

nuhkoca avatar May 07 '21 13:05 nuhkoca

@nuhkoca Not for now, this problem was postponed

sandwwraith avatar May 11 '21 12:05 sandwwraith

Wouldn't it be easy and logical to provide additional serializers to JsonBuilder? Currently only contextual and polyomorphic serializers can be provided...

angryziber avatar Dec 25 '21 13:12 angryziber

@angryziber This would provide serializers for Json format, but not for e.g. Protobuf. I assume this issue is about specifying a serializer for a type for a whole module for all formats

sandwwraith avatar Dec 27 '21 11:12 sandwwraith

Wouldn't it be easy and logical to provide additional serializers to JsonBuilder? Currently only contextual and polyomorphic serializers can be provided...

The "best" way would be to expose serializers in module metadata and then resolve them from this metadata. The question there is mainly how conflict resolution happens (which serializer should be used if there are multiple, and how can a developer override that - in some way the place would be some kind of module-info.kt)

pdvrieze avatar Dec 27 '21 11:12 pdvrieze

@sandwwraith as I understand, serializers are format-neutral, i.e. they describe the structure of the object and serialization can write/read different formats. I think I should have said SerializersModule, not JsonBuilder :-)

@pdvrieze I would even happily provide a list of global serializers to the kotlin-serialization plugin in build.gradle as a mapping of KClass -> Serializer, so that it could do it's compile time checking.

Anyway, SerializersModule.reflectiveOrContextual is quite inefficient currently, as it looks for a precompiled serializer using reflection every time. It should cache already found serializers to reuse them more quickly. Maybe this is the reason why kotlin-serialization is actually slower than other Java json libraries.

These two issues stop me from using the library, unfortunately. Back to Jackson that doesn't support inline classes properly...

angryziber avatar Dec 30 '21 17:12 angryziber

@angryziber What are you trying to achieve here? In particular, why are you using reflective or contextual serialization. The library is really designed for statically determining the serializer. However, it is reasonable that the lookup could be configured to use caching.

pdvrieze avatar Dec 30 '21 18:12 pdvrieze

@pdvrieze static serialization sounds good to me, but I want to avoid defining custom serializers for useful 3rd-party classes over and over again in every class/file. This is a serious violation of DRY principle. I want to do it once per my gradle project.

Contextual kind of works for this purpose, but it still requires writing @Contextual everywhere and it's actual purpose is different - dynamic, but global definition of static serializers is needed.

angryziber avatar Jan 05 '22 21:01 angryziber

is there any progress on this? either that or more built in serializers (for example BigDecimal, OffsetDateTime) would be really useful.

christophsturm avatar Apr 12 '22 16:04 christophsturm

@christophsturm No, this feature is postponed

sandwwraith avatar Apr 19 '22 12:04 sandwwraith

I feel that a config option could be added in the build.gradle(.kts).

Gaming32 avatar Sep 03 '22 13:09 Gaming32

@sandwwraith just a reminder that kolinx-serialization without this feature is unusable for a lot of developers on the JVM

angryziber avatar Sep 13 '22 10:09 angryziber

Shameless plug! I feel my project, KotlinX Serialization Standard Serializers, solves these ergonomic issues by providing readily defined typealiases and serializers for many common JVM types (and adding more is welcome!)

The original sample would simply become the following, with no further configuration or definition of serializers required.

data class Foo(val decimal: BigDecimalAsDouble, val id: UuidAsString, val date: LocalDateTimeAsString)

Project status is currently listed as alpha but from my experience using it for the past half year it's been working well for the use-cases I've had.

Kantis avatar Apr 27 '23 20:04 Kantis

The lack of java.time serializers out of the box makes it hard to demonstrate our open source library in Kotlin (in Kotlin Notebooks specifically). (Note that kotlinx.datetime is too weak for us, as it lacks the concept of Duration to represent the time between two LocalTime instances)

ge0ffrey avatar Jan 03 '24 09:01 ge0ffrey

@ge0ffrey I believe that LocalTime arithmetic is intentionally omitted from kotlinx-datetime for the same reason as LocalDateTime — it is hard to do it correctly when accounting for DST gaps and overlaps: https://github.com/Kotlin/kotlinx-datetime?tab=readme-ov-file#date--time-arithmetic. Therefore, a conversion to Instant is recommended.

Regarding java.time serializers, I believe that we can repurpose corresponding kotlinx-datetime serializers in some point of future.

sandwwraith avatar Jan 03 '24 13:01 sandwwraith