Alexey Zolotarev
02/06/2025, 8:42 AM@Serializable
sealed interface Wrapper
@Serializable
@JvmInline
value class StringWrapper(val value: String): Wrapper
@Serializable
@JvmInline
value class IntegerWrapper(val value: Int): Wrapper
@Serializable
@JvmInline
@OptIn(ExperimentalSerializationApi::class)
value class BinaryWrapper(@ByteString val value: ByteArray): Wrapper
class DataTreeValueSerializationTest {
@OptIn(ExperimentalSerializationApi::class, ExperimentalStdlibApi::class)
@Test
fun testSerialization() {
val data = IntegerWrapper(1)
val serialized = Cbor.encodeToByteArray(Wrapper.serializer(), data)
println(serialized.toHexString())
val deserialized = Cbor.decodeFromByteArray(Wrapper.serializer(), serialized)
assertEquals(data, deserialized)
}
}
This for example is encoded this way:
Diagnostic:
[
_ "IntegerWrapper",
1
]
Commented:
9f -- Array (streaming)
6e -- String, length: 14
496e746567657257726170706572 -- [0], "IntegerWrapper"
01 -- [1], 1
ff -- BREAK
0x9f6e496e74656765725772617070657201ff
And I would like to have it [1]
in this case. The interface is sealed and there is only one possible class that corresponds to an integer, IntegerWrapper
so it should be possible to encode/decode it without any ambiguity.
With JSON by the way this doesn't work at all since it encodes the data to "1" in this case but then expects an object on deserialization.Gergely Kőrössy
02/06/2025, 2:54 PMdata class Nice<T: @Serializable Any>(
@SerialName("id") val id: String,
@SerialName("type") val type: String,
// ... about 5 more parameters ...
@SerialName("value") val value: T,
)
Based on the type
parameter, the value could be a couple of serializable things: list of a data class(id,name) items, a single data class(id,name) item, or a boolean value.
• If I don't set a custom serializer with @Serializable(with = ...)
, the encoding to JSON works, but decoding from JSON runs into Star projections in type arguments are not allowed, but had null
error (trying to decode it as Nice<*>
.
• If I set a custom serializer by implementing JsonContentPolymorphicSerializer
I get Class 'Nice' is not registered for polymorphic serialization in the scope of 'Nice'.
even for encoding.
Is there any way to tell the deserializer that if the type
value is something, decode value
as either a list / single item / boolean without implementing a full on KSerializer? There are quite a few parameters in the class and would be nice not writing encode / decode statements for each one of them.Olaf Hoffmann
02/11/2025, 8:00 AMkotlinx.serialization
when trying to achieve
multiple levels of polymorphism with individual class discriminators for each level.
I'm building a library that allows users to deserialize JSON returned from a headless CMS. The CMS supplies a class
discriminator for every type returned in its API, but the types themselves are user-defined and may be polymorphic
themselves, which is what's causing me trouble.
Here's an abstract representation of some JSON that the CMS might return and that library should be able to deserialize.
[
{
"cmsDiscriminator": "someUserType",
"someUserValue": "value"
},
{
"cmsDiscriminator": "otherUserType",
"otherUserValue": "value"
}
]
The library only supplies the base class, setting up the cmsDiscriminator
.
@Serializable
@JsonClassDiscriminator("cmsDiscriminator")
abstract class CmsBase
Users then extend this base class with their own types which works fine for direct subclasses of CmsBase
.
@Serializable
@SerialName("someUserType")
class SomeUserType(val someUserValue: String) : CmsBase()
@Serializable
@SerialName("otherUserType")
class OtherUserType(val otherUserValue: String) : CmsBase()
The problem arises when the user wants to introduce a new polymorphic hierarchy, where the base class is a subclass of
CmsBase
and has its own class discriminator, for JSON like this
[
{
"cmsDiscriminator": "polymorphicUserType",
"userDiscriminator": "somePolymorphicSubType",
"someSubValue": "value"
},
{
"cmsDiscriminator": "polymorphicUserType",
"userDiscriminator": "otherPolymorphicSubType",
"otherSubValue": "value"
}
]
The users would have to define a class hierarchy like this:
@Serializable
@SerialName("polymorphicUserType")
@JsonClassDiscriminator("userDiscriminator") // doesn't work
abstract class UserPolymorphicBase : CmsBase()
@Serializable
@SerialName("somePolymorphicSubType")
class SomePolymorphicSubType(val someSubValue: String) : UserPolymorphicBase()
@Serializable
@SerialName("otherPolymorphicSubType")
class OtherPolymorphicSubType(val otherSubValue: String) : UserPolymorphicBase()
However, here's where I'm encountering my first problem: The @JsonClassDiscriminator
annotation can't be used on
UserPolymorphicBase
, because the Argument values for inheritable serial info annotation 'JsonClassDiscriminator'
must be the same as the values in parent type 'CmsBase'
, not allowing us to change the discriminator value for the
subclasses of some class in that hierarchy.
One way to get around that would be to have the user define and supply a JsonContentPolymorphicSerializer
for their
UserPolymorphicBase
class, but I would rather not force that on them. Additionally, it does not save me from the
second problem, which is that the library has to define a custom SerializerModule
since the CmsBase
class is not sealed
.
In the library, I would have to do something like this
serializersModule = SerializersModule {
polymorphic(CmsBase::class) {
subclass(SomeUserType::class)
subclass(OtherUserType::class)
subclass(UserPolymorphicBase::class) // doesn't work
}
polymorphic(UserPolymorphicBase::class) {
subclass(SomePolymorphicSubType::class)
subclass(OtherPolymorphicSubType::class)
}
}
But that fails because UserPolymorphicBase
is not a concrete class and thus can't be registered as a polymorphic subclass of CmsBase
.
This issue gets worse when considering the fact that the polymorphic hierarchy can be nested arbitrarily deep as far as
the user is concerned and should accordingly be supported by the library.
I believe this should be possible to do with a lot of workarounds, but I was hoping not to have to implement a bunch of
custom deserialization logic for this to work. Is there a way to achieve this with what we have available in
kotlinx.serialization
?
Thanks in advance for any help and suggestions!David Nault
02/11/2025, 6:03 PMserializerOrNull<T>()
couldn't be implemented as an intrinsic, or otherwise optimized? A bit of performance testing makes me suspect it's doing something expensive, on the order of throwing and catching an exception.HeYiming
02/17/2025, 2:36 AMZyle Moore
02/17/2025, 3:48 AMencodeSerializaleValue
of the Encoder interface
> Encodes the value of type T by delegating the encoding process to the given serializer. For example, encodeInt
call is equivalent to delegating integer encoding to Int.Companion.serializer: encodeSerializableValue(Int.serializer())
In the other Encoder examples I've seen, like JSON and Cbor and Protobuf, a different Encoder interface is created, which has different encode methods, like encodeTaggedInt
or encodeJsonElement
. Are these format-specific encode*
methods just strongly-typed convenience methods, preferred over encodeSerializableValue
? The documentation seems to indicate that even primitives could have an equivalent encodeSerializableValue
call, instead of an interface-specific encodeInt
method. Or at least, their effect on the Encoder should be the same.
The first part of the Encoder interface documentation says (my emphasis)
> Encoder is a core serialization primitive that encapsulates the knowledge of the underlying format and its storage, exposing only structural methods to the serializer, making it completely format-agnostic. Serialization process transforms a single value into the sequence of its primitive elements, also called its serial form, while encoding transforms these primitive elements into an actual format representation: JSON string, ProtoBuf ByteArray, in-memory map representation etc.
Is an encodeJsonElement
really a primitive? Should it be treated as an entrypoint into another serialization (since it should be equivalent to something like encodeSerializableValue(JsonElement)
)? Is an Int as "structural" as a JsonElement?cfleming
02/18/2025, 9:41 AMType.serializer()
methods. I have the serialization compiler plugin configured, both in my build and in the Kotlin Compiler settings in the IDE. The code compiles and runs fine both when built in the IDE and when run via the code compiled by my build, but the IDE just isn’t aware that the plugin has been applied. How does this work for Gradle? I had similar problems with Maven, it’s not just really esoteric builds which suffer from this. Is there a way I can configure this?Joshua Hansen
02/19/2025, 10:59 PMname
and a `value`:
sealed interface MyInterface<T : Any> {
val name: String
var value: T
}
There's a bunch of subclasses which implement this interface with different types:
class MyClass1(override var value: Boolean) : MyInterface<Boolean> {
override val name: String = "Foo"
}
class MyClass2(override var value: String) : MyInterface<String> {
override val name: String = "Bar"
}
I want to write a custom generic serializer which serializes these classes to/from a string of this format:
"$name=${/* String Serialized form of value */}"
I have a list of these in a parent class, like this:
class Parent {
val myList: List<MyInterface<*>> = listOf()
}
Is it possible to serialize/deserialize Parent
and ensure each member of the list is deserialized into the appropriate class instance? I'm not using JSON format. (I'm using KAML for YAML serialization)Rohde Fischer
02/20/2025, 1:20 PMsubdomain.error-group.specific-error
or similar. This kind of looks like it could be a hierarchy of interfaces and enums, but designing it feels... weird. So I'm wondering, has anyone approached a similar design? How do you use the types and their serialization to achieve this? Is the easiest actually a hierarchy with a single parent requirement? Or is there a better way to do this?cfleming
02/24/2025, 7:34 AMMatyáš Vítek
02/24/2025, 4:08 PMMap<Foo, String>
given that I have a serializer for Foo
? I really want to avoid writing a serializer for whole Map
classdawidhyzy
02/26/2025, 3:01 PM<lint>
<issue id="UnsafeOptInUsageError" severity="ignore">
<option name="opt-in" value="kotlinx.serialization.InternalSerializationApi" />
</issue>
</lint>
Robert Jaros
03/02/2025, 7:24 PMthrowLinkageError("Function 'encodeToString' can not be called: No function found for symbol 'kotlinx.serialization.json/Json.encodeToString|encodeToString(0:0){0\xA7<kotlin.Any?>}[0]'");
I'm getting this when upgrading a Kotlin/JS project to Kotlin 2.1.20-RC.James
03/05/2025, 12:46 PMT
can either be Nothing
or something user-defined (that also has a @Serializable
annotation).
Whenever I try to serialize it I get an error:
kotlinx.serialization.SerializationException: Serializer for class 'Health' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
All my data classes are defined with Serializable, so I'm unsure of how to go forward with this. I was thinking about writing a custom serializer that implements KSerializer. But I'm not entirely sure where to start as I haven't really looked into writing serializers before. I want to avoid doing anything JSON specific, as the type should work for other formats than JSON as well.
Any advice appreciated.Mehmet
03/07/2025, 9:11 AMBoolean
, without requiring explicit @Contextual
annotations on every field?Charann
03/10/2025, 5:26 AMfun testSerializer(){
val str = """{"doubleVal":1.725255546765E12}"""
val json = Json {
isLenient = true
}
val obj = json.decodeFromString<DoubleVal>(str)
println(obj)
}
@Serializable
class DoubleVal(val doubleVal: Long)
But I got an error "Unexpected JSON token at offset 13: Unexpected symbol '.' in numeric literal at path: $.doubleVal"
I tried using flags like "isLenient" but of no use. Am I missing any flags?krzysztof
03/13/2025, 9:16 AMserializer has not been found for type 'Uuid'
, but there is one built-in from serialization lib itself - how to properly mark Uuid as serializable?J
03/18/2025, 8:01 AMZyle Moore
04/01/2025, 12:19 AM@SerialInfo
annotation that indicates that the property is tagged with the given name, @Field("HEDR")
. When a property has this annotation, it is encoded as a Type-Length-Value structure. As an example,
@Field("HEDR")
val header: String = "asdf"
Results in bytes matching
TT TT TT TT LL LL VV VV VV VV
(HEDR 04 00 asdf
)
But, to get this to happen, I have to override the decode*Element
method for each primitive, to support each serializable type. Example,
@Field("HEDR")
val header: String = "asdf"
@Field("INTV")
val taggedValues: Int = 1
Is there an easy/cheap way to perform this tagging on any type, without overriding that specific type in the codec?CLOVIS
04/03/2025, 11:41 AM1
and another writes 1.0
). Could KotlinX.Serialization be used to deserialize multiple JSON representations and tell me if they are equal or not?hellman
04/04/2025, 7:10 AM@Transient
and they are calculated in the init block, but that doesn't seem to be allowed. It works if I change it to a lateinit var
but that seems wrong. Is this a bug in kotlinx-serialization or is there a workaround?Smoothie
04/07/2025, 1:50 PMLukasz Kalnik
04/07/2025, 2:55 PMJsonPrimitive
and it will work out-of-the-box?
@Serializable
data class Response(
val field: JsonPrimitive
)
Emil Kantis
04/08/2025, 9:43 AM{
"foo": { // irrelevant },
"bar": { // irrelevant },
} -> // [ "foo", "bar ]
Currently I got a
data class Wrapper(
val entries: Map<String, Irrelevant>
)
But I would like to discard/ignore the values of the mapNikky
04/11/2025, 7:22 AMAndrey Tabakov
04/21/2025, 1:13 PMrocketraman
04/25/2025, 1:37 PMdata class FooBar(val id: TypedUuid<FooBar>, …)
and the serialization of id
be as simple as fb_<uuid>
.Marcello Galhardo
04/28/2025, 8:29 PMserializer<T>()
that throws if no serializer is found, why isn’t there a serializerOrNull<T>()
as well? I know there’s a serializerOrNull(KType)
, but as I understand it, typeOf<T>()
relies on reflection and has performance implications.S.
05/01/2025, 10:27 AMJsonObject
to specify response formats of a third party lib, is it somehow possible to retrieve a JsonObject from a @Serializable class MyClass
without writing the mapping by hand?Zyle Moore
05/11/2025, 3:02 AMAny
and getting an error that no serializer for Any
was found. I understand why, I'm just not quite sure how to get what I want with that being true. By default, something like
@Serializable data class Point(val x: Int, val y: Int)
will eventually make a call to beginStructure
, two calls to encodeInt
, and a call to endStructure
. If I replace Int
with any other primitive type, like Short
, Double
, Float
, Long
, the two calls in the middle will be the appropriate method for that type, encodeShort
, encodeFloat
, etc. I'm looking for something that behaves exactly the same way, but the encode method it calls is encodeSerializableValue
instead of encodeInt
. Or maybe even encodeInline
. This example though doesn't use generics, so perhaps it's easier for the generator to map confidently.
In my generic wrapper example, I have a
@Serializable @JvmInline value class FieldValue<T : Any>(val value: T) : FieldToken
The idea being a very thin wrapper around some serializable value, that I can treat as a subtype of FieldToken
. I have a containing class
@Serializable data class Record(val header: RecordHeader, val fields: List<FieldValue<Any>>)
which is where my trouble comes in. I want to be able to serialize, roughly, something that looks like
[ "TES4", [ flags, formId, timestamp, versionControl, recordVersion, unknown ] ],// RecordHeader
[ "HEDR", [ 1.7, 0, 0 ] ],// // FieldValue<(Float, Short, Short)>
[ "CNAM", "Zymus" ],// FieldValue<NullTerminatedString>
[ "SNAM", "TES4 JSON Tuple Example" ],// FieldValue<NullTerminatedString>
[ "MAST", "Skyrim.esm" ],// FieldValue<NullterminatedString>
[ "DATA", 0 ],// FieldValue<Long>
[ "MAST", "Update.esm" ],// FieldValue<NullTerminatedString>
[ "DATA", 0 ],// FieldValue<Long>
[ "MAST", "Hearthfires.esm" ],// FieldValue<NullTerminatedString>
[ "DATA", 0 ],// FieldValue<Long>
[ "ONAM", [ 0 ] ],// FieldValue<List<Int>>
[ "INTV", 0 ],// FieldValue<Int>
[ "INCC", 0 ]// FieldValue<Short>