CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlinx--kotlinx-serialization-core-jvm

Kotlin multiplatform serialization runtime library core module with JVM target support

Pending
Overview
Eval results
Files

encoding.mddocs/

Encoding and Decoding

The encoding and decoding system provides the core interfaces that serialization formats use to read and write structured data. These interfaces abstract away format-specific details, allowing serializers to work with any format that implements the encoder/decoder contracts.

Capabilities

Core Encoding Interfaces

The fundamental interfaces for writing serialized data in a format-agnostic way.

/**
 * Core serialization primitive that encapsulates format-specific knowledge for writing data.
 * Provides methods for encoding primitive values and initiating structured encoding.
 */
interface Encoder {
    /** The serializers module for resolving contextual and polymorphic serializers */
    val serializersModule: SerializersModule
    
    /** Begin encoding a structured value, returning a CompositeEncoder for the structure */
    fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder
    
    /** Encode a boolean value */
    fun encodeBoolean(value: Boolean)
    
    /** Encode a byte value */
    fun encodeByte(value: Byte)
    
    /** Encode a char value */
    fun encodeChar(value: Char)
    
    /** Encode a double value */
    fun encodeDouble(value: Double)
    
    /** Encode an enum value */
    fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int)
    
    /** Encode a float value */
    fun encodeFloat(value: Float)
    
    /** Encode an inline value (value class) */
    fun encodeInline(descriptor: SerialDescriptor): Encoder
    
    /** Encode an int value */
    fun encodeInt(value: Int)
    
    /** Encode a long value */
    fun encodeLong(value: Long)
    
    /** Encode a null value */
    fun encodeNull()
    
    /** Encode a short value */
    fun encodeShort(value: Short)
    
    /** Encode a string value */
    fun encodeString(value: String)
}

/**
 * Part of the encoding process that handles structured data like classes, lists, and maps.
 * Bound to a specific structured part of the serialized form.
 */
interface CompositeEncoder {
    /** The serializers module for resolving contextual and polymorphic serializers */
    val serializersModule: SerializersModule
    
    /** Finish encoding the structure */
    fun endStructure(descriptor: SerialDescriptor)
    
    /** Check if element at given index should be encoded */
    fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean
    
    /** Encode a boolean element at the specified index */
    fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean)
    
    /** Encode a byte element at the specified index */
    fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte)
    
    /** Encode a char element at the specified index */
    fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char)
    
    /** Encode a double element at the specified index */
    fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double)
    
    /** Encode a float element at the specified index */
    fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float)
    
    /** Encode an inline element at the specified index */
    fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder
    
    /** Encode an int element at the specified index */
    fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int)
    
    /** Encode a long element at the specified index */
    fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long)
    
    /** Encode a null element at the specified index */
    fun encodeNullableSerializableElement(
        descriptor: SerialDescriptor, 
        index: Int, 
        serializer: SerializationStrategy<T>, 
        value: T?
    )
    
    /** Encode a serializable element at the specified index */
    fun encodeSerializableElement(
        descriptor: SerialDescriptor, 
        index: Int, 
        serializer: SerializationStrategy<T>, 
        value: T
    )
    
    /** Encode a short element at the specified index */
    fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short)
    
    /** Encode a string element at the specified index */
    fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String)
}

Core Decoding Interfaces

The fundamental interfaces for reading serialized data in a format-agnostic way.

/**
 * Core deserialization primitive for format-agnostic decoding of structured data.
 * Provides methods for decoding primitive values and initiating structured decoding.
 */
interface Decoder {
    /** The serializers module for resolving contextual and polymorphic serializers */
    val serializersModule: SerializersModule
    
    /** Begin decoding a structured value, returning a CompositeDecoder for the structure */
    fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder
    
    /** Decode a boolean value */
    fun decodeBoolean(): Boolean
    
    /** Decode a byte value */
    fun decodeByte(): Byte
    
    /** Decode a char value */
    fun decodeChar(): Char
    
    /** Decode a double value */
    fun decodeDouble(): Double
    
    /** Decode an enum value, returning the index */
    fun decodeEnum(enumDescriptor: SerialDescriptor): Int
    
    /** Decode a float value */
    fun decodeFloat(): Float
    
    /** Decode an inline value (value class) */
    fun decodeInline(descriptor: SerialDescriptor): Decoder
    
    /** Decode an int value */
    fun decodeInt(): Int
    
    /** Decode a long value */
    fun decodeLong(): Long
    
    /** Decode a null value, returning false if null */
    @ExperimentalSerializationApi
    fun decodeNotNullMark(): Boolean
    
    /** Decode a null value */
    @ExperimentalSerializationApi
    fun decodeNull(): Nothing?
    
    /** Decode a short value */
    fun decodeShort(): Short
    
    /** Decode a string value */
    fun decodeString(): String
}

/**
 * Part of the decoding process that handles structured data like classes, lists, and maps.
 * Bound to a specific structured part of the serialized form.
 */
interface CompositeDecoder {
    /** The serializers module for resolving contextual and polymorphic serializers */
    val serializersModule: SerializersModule
    
    /** Finish decoding the structure */
    fun endStructure(descriptor: SerialDescriptor)
    
    /** Decode the next element index, returning CompositeDecoder.DECODE_DONE when finished */
    fun decodeElementIndex(descriptor: SerialDescriptor): Int
    
    /** Decode a boolean element at the specified index */
    fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean
    
    /** Decode a byte element at the specified index */
    fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte
    
    /** Decode a char element at the specified index */
    fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char
    
    /** Decode a double element at the specified index */
    fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double
    
    /** Decode a float element at the specified index */
    fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float
    
    /** Decode an inline element at the specified index */
    fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder
    
    /** Decode an int element at the specified index */
    fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int
    
    /** Decode a long element at the specified index */
    fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long
    
    /** Decode a nullable serializable element at the specified index */
    fun decodeNullableSerializableElement(
        descriptor: SerialDescriptor, 
        index: Int, 
        deserializer: DeserializationStrategy<T?>, 
        previousValue: T? = null
    ): T?
    
    /** Decode a serializable element at the specified index */
    fun decodeSerializableElement(
        descriptor: SerialDescriptor, 
        index: Int, 
        deserializer: DeserializationStrategy<T>, 
        previousValue: T? = null
    ): T
    
    /** Decode a short element at the specified index */
    fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short
    
    /** Decode a string element at the specified index */
    fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String
    
    companion object {
        /** Constant indicating that decoding is complete */
        const val DECODE_DONE: Int = -1
        
        /** Constant indicating unknown element index */
        const val UNKNOWN_NAME: Int = -3
    }
}

Abstract Base Classes

Skeleton implementations that provide common functionality for format implementations.

/**
 * Skeleton implementation of both Encoder and CompositeEncoder.
 * Provides default implementations for common encoding patterns.
 */
abstract class AbstractEncoder : Encoder, CompositeEncoder {
    /** Default implementation delegates to CompositeEncoder methods */
    final override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = this
    
    /** Default implementation calls endStructure */
    override fun endStructure(descriptor: SerialDescriptor) {}
    
    /** Default implementation returns true for required elements, false for optional */
    override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean =
        !descriptor.isElementOptional(index)
}

/**
 * Skeleton implementation of both Decoder and CompositeDecoder.
 * Provides default implementations for common decoding patterns.
 */
abstract class AbstractDecoder : Decoder, CompositeDecoder {
    /** Default implementation delegates to CompositeDecoder methods */
    final override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = this
    
    /** Default implementation calls endStructure */
    override fun endStructure(descriptor: SerialDescriptor) {}
}

Specialized Interfaces

Additional interfaces for specific encoding/decoding scenarios.

/**
 * Decoder interface that supports consuming large strings by chunks.
 * Useful for streaming formats that need to process large text data efficiently.
 */
interface ChunkedDecoder {
    /**
     * Decodes a string value by chunks, calling the consumer for each chunk.
     * @param consumeChunk Function to consume each string chunk
     */
    fun decodeStringChunked(consumeChunk: (chunk: String) -> Unit)
}

Encoding Extension Functions

Convenient extension functions that simplify common encoding patterns.

/**
 * Encodes structured values using CompositeEncoder in a convenient DSL style.
 * @param descriptor The descriptor for the structure being encoded
 * @param block Lambda that performs the encoding using CompositeEncoder
 */
inline fun Encoder.encodeStructure(
    descriptor: SerialDescriptor,
    crossinline block: CompositeEncoder.() -> Unit
)

/**
 * Convenience method for encoding collections with consistent element handling.
 * @param descriptor The descriptor for the collection
 * @param collection The collection to encode
 * @param block Lambda to encode each collection element
 */
inline fun <T> Encoder.encodeCollection(
    descriptor: SerialDescriptor,
    collection: Collection<T>,
    crossinline block: CompositeEncoder.(index: Int, element: T) -> Unit
)

Decoding Extension Functions

Convenient extension functions that simplify common decoding patterns.

/**
 * Decodes structured values using CompositeDecoder in a convenient DSL style.
 * @param descriptor The descriptor for the structure being decoded
 * @param block Lambda that performs the decoding using CompositeDecoder
 * @return The decoded value
 */
inline fun <T> Decoder.decodeStructure(
    descriptor: SerialDescriptor,
    crossinline block: CompositeDecoder.() -> T
): T

Usage Examples:

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*

// Custom serializer using encoding interfaces
object PointSerializer : KSerializer<Point> {
    override val descriptor = buildClassSerialDescriptor("Point") {
        element("x", PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT))
        element("y", PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT))
    }
    
    override fun serialize(encoder: Encoder, value: Point) {
        encoder.encodeStructure(descriptor) {
            encodeIntElement(descriptor, 0, value.x)
            encodeIntElement(descriptor, 1, value.y)
        }
    }
    
    override fun deserialize(decoder: Decoder): Point {
        return decoder.decodeStructure(descriptor) {
            var x = 0
            var y = 0
            
            while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> x = decodeIntElement(descriptor, index)
                    1 -> y = decodeIntElement(descriptor, index)
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unexpected index: $index")
                }
            }
            
            Point(x, y)
        }
    }
}

Implementation Patterns

Format Implementation

When implementing a new format, you typically extend the abstract base classes:

import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*

class MyFormatEncoder : AbstractEncoder() {
    override val serializersModule: SerializersModule = EmptySerializersModule
    
    override fun encodeString(value: String) {
        // Format-specific string encoding
    }
    
    override fun encodeInt(value: Int) {
        // Format-specific int encoding  
    }
    
    // Implement other encoding methods...
}

class MyFormatDecoder : AbstractDecoder() {
    override val serializersModule: SerializersModule = EmptySerializersModule
    
    override fun decodeString(): String {
        // Format-specific string decoding
    }
    
    override fun decodeInt(): Int {
        // Format-specific int decoding
    }
    
    // Implement other decoding methods...
}

Sequential vs Random Access

Different formats may require different decoding approaches:

// Sequential decoding (streaming formats)
override fun deserialize(decoder: Decoder): MyClass {
    return decoder.decodeStructure(descriptor) {
        var field1: String? = null
        var field2: Int? = null
        
        while (true) {
            when (val index = decodeElementIndex(descriptor)) {
                0 -> field1 = decodeStringElement(descriptor, index)
                1 -> field2 = decodeIntElement(descriptor, index)
                CompositeDecoder.DECODE_DONE -> break
                else -> error("Unexpected index: $index")
            }
        }
        
        MyClass(field1!!, field2!!)
    }
}

// Random access decoding (structured formats like JSON objects)
override fun deserialize(decoder: Decoder): MyClass {
    return decoder.decodeStructure(descriptor) {
        MyClass(
            decodeStringElement(descriptor, 0),
            decodeIntElement(descriptor, 1)
        )
    }
}

Error Handling

Common encoding/decoding errors and their causes:

  • SerializationException: General encoding/decoding failures
  • IllegalStateException: Invalid encoder/decoder state transitions
  • IndexOutOfBoundsException: Invalid element index access
  • ClassCastException: Type mismatches during decoding

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-serialization-core-jvm

docs

annotations.md

built-ins.md

core-serialization.md

descriptors.md

encoding.md

index.md

modules.md

tile.json