Encoder

interface Encoder

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.

Encoder provides high-level API that operates with basic primitive types, collections and nested structures. Internally, encoder represents output storage and operates with its state and lower level format-specific details.

To be more specific, serialization transforms a value into a sequence of "here is an int, here is a double, here a list of strings and here is another object that is a nested int", while encoding transforms this sequence into a format-specific commands such as "insert opening curly bracket for a nested object start, insert a name of the value, and the value separated with colon for an int etc."

The symmetric interface for the deserialization process is Decoder.

Serialization. Primitives

If a class is represented as a single primitive value in its serialized form, then one of the encode* methods (e.g. encodeInt) can be used directly.

Serialization. Structured types.

If a class is represented as a structure or has multiple values in its serialized form, encode* methods are not that helpful, because they do not allow working with collection types or establish structure boundaries. All these capabilities are delegated to the CompositeEncoder interface with a more specific API surface. To denote a structure start, beginStructure should be used.

// Denote the structure start,
val composite = encoder.beginStructure(descriptor)
// Encoding all elements within the structure using 'composite'
...
// Denote the structure end
composite.endStructure(descriptor)

E.g. if the encoder belongs to JSON format, then beginStructure will write an opening bracket ({ or [, depending on the descriptor kind), returning the CompositeEncoder that is aware of colon separator, that should be appended between each key-value pair, whilst CompositeEncoder.endStructure will write a closing bracket.

Exception guarantees.

For the regular exceptions, such as invalid input, conflicting serial names, SerializationException can be thrown by any encoder methods. It is recommended to declare a format-specific subclass of SerializationException and throw it.

Format encapsulation

For example, for the following serializer:

class StringHolder(val stringValue: String)

object StringPairDeserializer : SerializationStrategy<StringHolder> {
override val descriptor = ...

override fun serializer(encoder: Encoder, value: StringHolder) {
// Denotes start of the structure, StringHolder is not a "plain" data type
val composite = encoder.beginStructure(descriptor)
// Encode the nested string value
composite.encodeStringElement(descriptor, index = 0)
// Denotes end of the structure
composite.endStructure(descriptor)
}
}

This serializer does not know anything about the underlying storage and will work with any properly-implemented encoder. JSON, for example, writes an opening bracket { during the beginStructure call, writes 'stringValuekey along with its value in encodeStringElementand writes the closing bracket }during the endStructure`. XML would do roughly the same, but with different separators and structures, while ProtoBuf machinery could be completely different. In any case, all these parsing details are encapsulated by an encoder.

Exception safety

In general, catching SerializationException from any of encode* methods is not allowed and produces unspecified behaviour. After thrown exception, current encoder is left in an arbitrary state, no longer suitable for further encoding.

Encoder implementation.

While being strictly typed, an underlying format can transform actual types in the way it wants. For example, a format can support only string types and encode/decode all primitives in a string form:

StringFormatEncoder : Encoder {

...
override fun encodeDouble(value: Double) = encodeString(value.toString())
override fun encodeInt(value: Int) = encodeString(value.toString())
...
}

Not stable for inheritance

Encoder interface is not stable for inheritance in 3rd party libraries, as new methods might be added to this interface or contracts of the existing methods can be changed.

Functions

Link copied to clipboard
open fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder

Encodes the beginning of the collection with size collectionSize and the given serializer of its type parameters. This method has to be implemented only if you need to know collection size in advance, otherwise, beginStructure can be used.

Link copied to clipboard
abstract fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder

Encodes the beginning of the nested structure in a serialized form and returns CompositeDecoder responsible for encoding this very structure. E.g the hierarchy:

Link copied to clipboard
abstract fun encodeBoolean(value: Boolean)

Encodes a boolean value. Corresponding kind is PrimitiveKind.BOOLEAN.

Link copied to clipboard
abstract fun encodeByte(value: Byte)

Encodes a single byte value. Corresponding kind is PrimitiveKind.BYTE.

Link copied to clipboard
abstract fun encodeChar(value: Char)

Encodes a 16-bit unicode character value. Corresponding kind is PrimitiveKind.CHAR.

Link copied to clipboard
abstract fun encodeDouble(value: Double)

Encodes a 64-bit IEEE 754 floating point value. Corresponding kind is PrimitiveKind.DOUBLE.

Link copied to clipboard
abstract fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int)

Encodes a enum value that is stored at the index in enumDescriptor elements collection. Corresponding kind is SerialKind.ENUM.

Link copied to clipboard
abstract fun encodeFloat(value: Float)

Encodes a 32-bit IEEE 754 floating point value. Corresponding kind is PrimitiveKind.FLOAT.

Link copied to clipboard
abstract fun encodeInline(descriptor: SerialDescriptor): Encoder

Returns Encoder for encoding an underlying type of a value class in an inline manner. descriptor describes a serializable value class.

Link copied to clipboard
abstract fun encodeInt(value: Int)

Encodes a 32-bit integer value. Corresponding kind is PrimitiveKind.INT.

Link copied to clipboard
abstract fun encodeLong(value: Long)

Encodes a 64-bit integer value. Corresponding kind is PrimitiveKind.LONG.

Link copied to clipboard
open fun encodeNotNullMark()

Notifies the encoder that value of a nullable type that is being serialized is not null. It should be called before writing a non-null value of nullable type:

Link copied to clipboard
abstract fun encodeNull()

Encodes null value.

Link copied to clipboard
open fun <T : Any> encodeNullableSerializableValue(serializer: SerializationStrategy<T>, value: T?)

Encodes the nullable value of type T by delegating the encoding process to the given serializer.

Link copied to clipboard
open fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T)

Encodes the value of type T by delegating the encoding process to the given serializer. For example, encodeInt call us equivalent to delegating integer encoding to Int.serializer: encodeSerializableValue(Int.serializer())

Link copied to clipboard
abstract fun encodeShort(value: Short)

Encodes a 16-bit short value. Corresponding kind is PrimitiveKind.SHORT.

Link copied to clipboard
abstract fun encodeString(value: String)

Encodes a string value. Corresponding kind is PrimitiveKind.STRING.

Properties

Link copied to clipboard
abstract val serializersModule: SerializersModule

Context of the current serialization process, including contextual and polymorphic serialization and, potentially, a format-specific configuration.

Inheritors

Link copied to clipboard

Extensions

Link copied to clipboard
inline fun Encoder.encodeCollection(    descriptor: SerialDescriptor,     collectionSize: Int,     crossinline block: CompositeEncoder.() -> Unit)

Begins a collection, encodes it using the given block and ends it.

inline fun <E> Encoder.encodeCollection(    descriptor: SerialDescriptor,     collection: Collection<E>,     crossinline block: CompositeEncoder.(index: Int, E) -> Unit)

Begins a collection, calls block with each item and ends the collections.

Link copied to clipboard
inline fun Encoder.encodeStructure(descriptor: SerialDescriptor, crossinline block: CompositeEncoder.() -> Unit)

Begins a structure, encodes it using the given block and ends it.