Search code examples
kotlinserializationannotationskotlinx.serialization

@SerialInfo - How to manage user-defined serial annotations with Kotlinx serialization?


Kotlinx serialization documentation

According to Kotlinx.serialization user-defined annotations doc:

"Inside a process of serialization/deserialization, your own annotation class are available in SerialDescriptor object" :

override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean {
    val annotations = desc.getElementAnnotations(index)
    ...
}

What I want to do

I need a @Transient equivalent, but conditional:

  • classic way where : Json.stringify(serializer, myClass) works as usual.
  • custom way where : Json.stringify(customSerializer, myClass) would return usual json but exculding all @MyAnnotation-tagged values.

Here is my code

@SerialInfo
@Target(AnnotationTarget.PROPERTY)
annotation class CustomAnnotation

@Serializable
data class MyClass(val a: String, @CustomAnnotation val b: Int = -1)

And I would like to build a custom Serializer and achieve something like

override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean {
    val isTaggedAsCustomAnnotation = desc.getElementAnnotations(index).any{ it is CustomAnnotation }
    val myCondition = mySerializer.getMyConditionBlablabla

    if(myCondition && isTaggedAsCustomAnnotation) {
        encode()    
    }
    ...
}

What I found

abstract class ElementValueEncoder : Encoder, CompositeEncoder {
    ...
    open fun encodeElement(desc: SerialDescriptor, index: Int): Boolean = true
}

But I don't know how I can build a custom Serializer so that I can override that function Encoder.encodeElement. Where can I access to ElementValueEncoder in a custom Serializer ?

I also found this sample demo in kotlinx.serialization github repo. It's using TaggedEncoder & TaggedDecoder where I'm able to override encodeTaggedValue. But here again I don't know how I can use those encoder/decoder in a process of serialization/deserialization.

Finally

Where can I override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean, and how I can handle my own-defined serialization annotation ?

Thanks !!


Solution

  • First of all, you need to grasp the difference between Serializer and Encoder. Serializer (represented by KSerializer) defines how your class looks like, and Encoder (represented by e.g. JsonOutput) defines how data will be recorded. You can find more info on that topic here: https://github.com/Kotlin/KEEP/blob/master/proposals/extensions/serialization.md#core-api-overview-and-mental-model .

    So, custom annotations feature is mainly used for providing format-specific information to Encoder. Typical usage of such an annotation is ProtoId – property id, specific to protobuf format, that should be recognized by ProtobufEncoder. Such annotations are usually defined by format authors alongside their encoders.

    What you want to do here, as I can see, is to use already existing encoder (JSON format), so overriding encodeElement is impossible since Json encoders can not be subclassed. I'd advise you to use custom json transofrming serializer to achieve your goal. Unfortunately, currently kotlinx.serialization does not have mechanism to generalize such a transformation, so you need to write such serializer for each class.