I am writing a custom format encoder based on AbstractEncoder and would like to serialize this type of data:
@Serializable
class MyData(
val data: String? = "dummy"
)
I would like to serialize the data element only when it is not null, which is different than the default value, but currently cannot do that using the AbstractEncoder API.
Similarly during decoding, if the input stream does not contain the data entry, I would like the data value to be deserialized as null.
This function of CompositeEncoder could have version that takes value as an additional parameter:
public fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = true
This function implemented in AbstractEncoder could be non final:
public fun <T : Any> encodeNullableSerializableElement(
descriptor: SerialDescriptor,
index: Int,
serializer: SerializationStrategy<T>,
value: T?
)
Hi,
Unfortunately, option number 1. is a no-go — introducing generic shouldEncodeElementDefault will lead to a reduced performance in cases where the value is a primitive type (due to boxing). However, what you probably want is to override encodeNull and to capture current descriptor in encodeElement beforehand. Indeed, it is not very convenient, so we may add descriptor directly to encodeNull in future.
Regarding decoding, first I have to say that your idea is misaligned with framework design: we prefer to think that deserializer is just another constructor for object, so if value was not provided, then a default one should be set ("dummy", but not null).
However, you can still achieve this if you track all missing values in decodeElementIndex. Then, you can report them to serializer as non-missing and use decodeNotNullMark() = false.
Hi,
Thank you very much for your comment.
I understand that this approach is not fully aligned with the framework.
The format I am trying to serialize is an external format with its own semantics where sometimes missing value is just another value.
Maybe the framework could use another "semantic" layer besides format and structure, where the default behaviour would be "it works like a constructor". Currently this (rather thin) layer responsibility for "carrying meaning" will probably have to go to the Encoder.
The need for these three orthogonal aspects of serialization (data, semantics and format) emerged for me when I wrote by hand my custom serialization tool using reflection.
It looks to me that this "semantic" layer already lurks in the framework in the form of annotations like @Required or @Transient. I am aware of the problem of over-generalization and the difficulty of finding good abstractions when there is little data to abstract over, so these may be just my ramblings :).