/* * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.serialization import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* /** * KSerializer is responsible for the representation of a serial form of a type [T] * in terms of [encoders][Encoder] and [decoders][Decoder] and for constructing and deconstructing [T] * from/to a sequence of encoding primitives. For classes marked with [@Serializable][Serializable], can be * obtained from generated companion extension `.serializer()` or from [serializer()][serializer] function. * * Serialization is decoupled from the encoding process to make it completely format-agnostic. * Serialization represents a type as its serial form and is abstracted from the actual * format (whether its JSON, ProtoBuf or a hashing) and unaware of the underlying storage * (whether it is a string builder, byte array or a network socket), while * encoding/decoding is abstracted from a particular type and its serial form and is responsible * for transforming primitives ("here in an int property 'foo'" call from a serializer) into a particular * format-specific representation ("for a given int, append a property name in quotation marks, * then append a colon, then append an actual value" for JSON) and how to retrieve a primitive * ("give me an int that is 'foo' property") from the underlying representation ("expect the next string to be 'foo', * parse it, then parse colon, then parse a string until the next comma as an int and return it). * * Serial form consists of a structural description, declared by the [descriptor] and * actual serialization and deserialization processes, defined by the corresponding * [serialize] and [deserialize] methods implementation. * * Structural description specifies how the [T] is represented in the serial form: * its [kind][SerialKind] (e.g. whether it is represented as a primitive, a list or a class), * its [elements][SerialDescriptor.elementNames] and their [positional names][SerialDescriptor.getElementName]. * * Serialization process is defined as a sequence of calls to an [Encoder], and transforms a type [T] * into a stream of format-agnostic primitives that represent [T], such as "here is an int, here is a double * and here is another nested object". It can be demonstrated by the example: * ``` * class MyData(int: Int, stringList: List, alwaysZero: Long) * * // .. serialize method of a corresponding serializer * fun serialize(encoder: Encoder, value: MyData): Unit = encoder.encodeStructure(descriptor) { * // encodeStructure encodes beginning and end of the structure * // encode 'int' property as Int * encodeIntElement(descriptor, index = 0, value.int) * // encode 'stringList' property as List * encodeSerializableElement(descriptor, index = 1, serializer>, value.stringList) * // don't encode 'alwaysZero' property because we decided to do so * } // end of the structure * ``` * * Deserialization process is symmetric and uses [Decoder]. * * ### Exception types for `KSerializer` implementation * * Implementations of [serialize] and [deserialize] methods are allowed to throw * any subtype of [IllegalArgumentException] in order to indicate serialization * and deserialization errors. * * For serializer implementations, it is recommended to throw subclasses of [SerializationException] for * any serialization-specific errors related to invalid or unsupported format of the data * and [IllegalStateException] for errors during validation of the data. */ public interface KSerializer : SerializationStrategy, DeserializationStrategy { /** * Describes the structure of the serializable representation of [T], produced * by this serializer. Knowing the structure of the descriptor is required to determine * the shape of the serialized form (e.g. what elements are encoded as lists and what as primitives) * along with its metadata such as alternative names. * * The descriptor is used during serialization by encoders and decoders * to introspect the type and metadata of [T]'s elements being encoded or decoded, and * to introspect the type, infer the schema or to compare against the predefined schema. */ override val descriptor: SerialDescriptor } /** * Serialization strategy defines the serial form of a type [T], including its structural description, * declared by the [descriptor] and the actual serialization process, defined by the implementation * of the [serialize] method. * * [serialize] method takes an instance of [T] and transforms it into its serial form (a sequence of primitives), * calling the corresponding [Encoder] methods. * * A serial form of the type is a transformation of the concrete instance into a sequence of primitive values * and vice versa. The serial form is not required to completely mimic the structure of the class, for example, * a specific implementation may represent multiple integer values as a single string, omit or add some * values that are present in the type, but not in the instance. * * For a more detailed explanation of the serialization process, please refer to [KSerializer] documentation. */ public interface SerializationStrategy { /** * Describes the structure of the serializable representation of [T], produced * by this serializer. */ public val descriptor: SerialDescriptor /** * Serializes the [value] of type [T] using the format that is represented by the given [encoder]. * [serialize] method is format-agnostic and operates with a high-level structured [Encoder] API. * Throws [SerializationException] if value cannot be serialized. * * Example of serialize method: * ``` * class MyData(int: Int, stringList: List, alwaysZero: Long) * * fun serialize(encoder: Encoder, value: MyData): Unit = encoder.encodeStructure(descriptor) { * // encodeStructure encodes beginning and end of the structure * // encode 'int' property as Int * encodeIntElement(descriptor, index = 0, value.int) * // encode 'stringList' property as List * encodeSerializableElement(descriptor, index = 1, serializer>, value.stringList) * // don't encode 'alwaysZero' property because we decided to do so * } // end of the structure * ``` * * @throws SerializationException in case of any serialization-specific error * @throws IllegalArgumentException if the supplied input does not comply encoder's specification * @see KSerializer for additional information about general contracts and exception specifics */ public fun serialize(encoder: Encoder, value: T) } /** * Deserialization strategy defines the serial form of a type [T], including its structural description, * declared by the [descriptor] and the actual deserialization process, defined by the implementation * of the [deserialize] method. * * [deserialize] method takes an instance of [Decoder], and, knowing the serial form of the [T], * invokes primitive retrieval methods on the decoder and then transforms the received primitives * to an instance of [T]. * * A serial form of the type is a transformation of the concrete instance into a sequence of primitive values * and vice versa. The serial form is not required to completely mimic the structure of the class, for example, * a specific implementation may represent multiple integer values as a single string, omit or add some * values that are present in the type, but not in the instance. * * For a more detailed explanation of the serialization process, please refer to [KSerializer] documentation. */ public interface DeserializationStrategy { /** * Describes the structure of the serializable representation of [T], that current * deserializer is able to deserialize. */ public val descriptor: SerialDescriptor /** * Deserializes the value of type [T] using the format that is represented by the given [decoder]. * [deserialize] method is format-agnostic and operates with a high-level structured [Decoder] API. * As long as most of the formats imply an arbitrary order of properties, deserializer should be able * to decode these properties in an arbitrary order and in a format-agnostic way. * For that purposes, [CompositeDecoder.decodeElementIndex]-based loop is used: decoder firstly * signals property at which index it is ready to decode and then expects caller to decode * property with the given index. * * Throws [SerializationException] if value cannot be deserialized. * * Example of deserialize method: * ``` * class MyData(int: Int, stringList: List, alwaysZero: Long) * * fun deserialize(decoder: Decoder): MyData = decoder.decodeStructure(descriptor) { * // decodeStructure decodes beginning and end of the structure * var int: Int? = null * var list: List? = null * loop@ while (true) { * when (val index = decodeElementIndex(descriptor)) { * DECODE_DONE -> break@loop * 0 -> { * // Decode 'int' property as Int * int = decodeIntElement(descriptor, index = 0) * } * 1 -> { * // Decode 'stringList' property as List * list = decodeSerializableElement(descriptor, index = 1, serializer>()) * } * else -> throw SerializationException("Unexpected index $index") * } * } * if (int == null || list == null) throwMissingFieldException() * // Always use 0 as a value for alwaysZero property because we decided to do so. * return MyData(int, list, alwaysZero = 0L) * } * ``` * * @throws MissingFieldException if non-optional fields were not found during deserialization * @throws SerializationException in case of any deserialization-specific error * @throws IllegalArgumentException if the decoded input is not a valid instance of [T] * @see KSerializer for additional information about general contracts and exception specifics */ public fun deserialize(decoder: Decoder): T }