xref: /aosp_15_r20/external/kotlinx.serialization/core/commonMain/src/kotlinx/serialization/KSerializer.kt (revision 57b5a4a64c534cf7f27ac9427ceab07f3d8ed3d8)
1 /*
2  * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization
6 
7 import kotlinx.serialization.descriptors.*
8 import kotlinx.serialization.encoding.*
9 
10 /**
11  * KSerializer is responsible for the representation of a serial form of a type [T]
12  * in terms of [encoders][Encoder] and [decoders][Decoder] and for constructing and deconstructing [T]
13  * from/to a sequence of encoding primitives. For classes marked with [@Serializable][Serializable], can be
14  * obtained from generated companion extension `.serializer()` or from [serializer<T>()][serializer] function.
15  *
16  * Serialization is decoupled from the encoding process to make it completely format-agnostic.
17  * Serialization represents a type as its serial form and is abstracted from the actual
18  * format (whether its JSON, ProtoBuf or a hashing) and unaware of the underlying storage
19  * (whether it is a string builder, byte array or a network socket), while
20  * encoding/decoding is abstracted from a particular type and its serial form and is responsible
21  * for transforming primitives ("here in an int property 'foo'" call from a serializer) into a particular
22  * format-specific representation ("for a given int, append a property name in quotation marks,
23  * then append a colon, then append an actual value" for JSON) and how to retrieve a primitive
24  * ("give me an int that is 'foo' property") from the underlying representation ("expect the next string to be 'foo',
25  * parse it, then parse colon, then parse a string until the next comma as an int and return it).
26  *
27  * Serial form consists of a structural description, declared by the [descriptor] and
28  * actual serialization and deserialization processes, defined by the corresponding
29  * [serialize] and [deserialize] methods implementation.
30  *
31  * Structural description specifies how the [T] is represented in the serial form:
32  * its [kind][SerialKind] (e.g. whether it is represented as a primitive, a list or a class),
33  * its [elements][SerialDescriptor.elementNames] and their [positional names][SerialDescriptor.getElementName].
34  *
35  * Serialization process is defined as a sequence of calls to an [Encoder], and transforms a type [T]
36  * into a stream of format-agnostic primitives that represent [T], such as "here is an int, here is a double
37  * and here is another nested object". It can be demonstrated by the example:
38  * ```
39  * class MyData(int: Int, stringList: List<String>, alwaysZero: Long)
40  *
41  * // .. serialize method of a corresponding serializer
42  * fun serialize(encoder: Encoder, value: MyData): Unit = encoder.encodeStructure(descriptor) {
43  *     // encodeStructure encodes beginning and end of the structure
44  *     // encode 'int' property as Int
45  *     encodeIntElement(descriptor, index = 0, value.int)
46  *     // encode 'stringList' property as List<String>
47  *     encodeSerializableElement(descriptor, index = 1, serializer<List<String>>, value.stringList)
48  *     // don't encode 'alwaysZero' property because we decided to do so
49  * } // end of the structure
50  * ```
51  *
52  * Deserialization process is symmetric and uses [Decoder].
53  *
54  * ### Exception types for `KSerializer` implementation
55  *
56  * Implementations of [serialize] and [deserialize] methods are allowed to throw
57  * any subtype of [IllegalArgumentException] in order to indicate serialization
58  * and deserialization errors.
59  *
60  * For serializer implementations, it is recommended to throw subclasses of [SerializationException] for
61  * any serialization-specific errors related to invalid or unsupported format of the data
62  * and [IllegalStateException] for errors during validation of the data.
63  */
64 public interface KSerializer<T> : SerializationStrategy<T>, DeserializationStrategy<T> {
65     /**
66      * Describes the structure of the serializable representation of [T], produced
67      * by this serializer. Knowing the structure of the descriptor is required to determine
68      * the shape of the serialized form (e.g. what elements are encoded as lists and what as primitives)
69      * along with its metadata such as alternative names.
70      *
71      * The descriptor is used during serialization by encoders and decoders
72      * to introspect the type and metadata of [T]'s elements being encoded or decoded, and
73      * to introspect the type, infer the schema or to compare against the predefined schema.
74      */
75     override val descriptor: SerialDescriptor
76 }
77 
78 /**
79  * Serialization strategy defines the serial form of a type [T], including its structural description,
80  * declared by the [descriptor] and the actual serialization process, defined by the implementation
81  * of the [serialize] method.
82  *
83  * [serialize] method takes an instance of [T] and transforms it into its serial form (a sequence of primitives),
84  * calling the corresponding [Encoder] methods.
85  *
86  * A serial form of the type is a transformation of the concrete instance into a sequence of primitive values
87  * and vice versa. The serial form is not required to completely mimic the structure of the class, for example,
88  * a specific implementation may represent multiple integer values as a single string, omit or add some
89  * values that are present in the type, but not in the instance.
90  *
91  * For a more detailed explanation of the serialization process, please refer to [KSerializer] documentation.
92  */
93 public interface SerializationStrategy<in T> {
94     /**
95      * Describes the structure of the serializable representation of [T], produced
96      * by this serializer.
97      */
98     public val descriptor: SerialDescriptor
99 
100     /**
101      * Serializes the [value] of type [T] using the format that is represented by the given [encoder].
102      * [serialize] method is format-agnostic and operates with a high-level structured [Encoder] API.
103      * Throws [SerializationException] if value cannot be serialized.
104      *
105      * Example of serialize method:
106      * ```
107      * class MyData(int: Int, stringList: List<String>, alwaysZero: Long)
108      *
109      * fun serialize(encoder: Encoder, value: MyData): Unit = encoder.encodeStructure(descriptor) {
110      *     // encodeStructure encodes beginning and end of the structure
111      *     // encode 'int' property as Int
112      *     encodeIntElement(descriptor, index = 0, value.int)
113      *     // encode 'stringList' property as List<String>
114      *     encodeSerializableElement(descriptor, index = 1, serializer<List<String>>, value.stringList)
115      *     // don't encode 'alwaysZero' property because we decided to do so
116      * } // end of the structure
117      * ```
118      *
119      * @throws SerializationException in case of any serialization-specific error
120      * @throws IllegalArgumentException if the supplied input does not comply encoder's specification
121      * @see KSerializer for additional information about general contracts and exception specifics
122      */
serializenull123     public fun serialize(encoder: Encoder, value: T)
124 }
125 
126 /**
127  * Deserialization strategy defines the serial form of a type [T], including its structural description,
128  * declared by the [descriptor] and the actual deserialization process, defined by the implementation
129  * of the [deserialize] method.
130  *
131  * [deserialize] method takes an instance of [Decoder], and, knowing the serial form of the [T],
132  * invokes primitive retrieval methods on the decoder and then transforms the received primitives
133  * to an instance of [T].
134  *
135  * A serial form of the type is a transformation of the concrete instance into a sequence of primitive values
136  * and vice versa. The serial form is not required to completely mimic the structure of the class, for example,
137  * a specific implementation may represent multiple integer values as a single string, omit or add some
138  * values that are present in the type, but not in the instance.
139  *
140  * For a more detailed explanation of the serialization process, please refer to [KSerializer] documentation.
141  */
142 public interface DeserializationStrategy<out T> {
143     /**
144      * Describes the structure of the serializable representation of [T], that current
145      * deserializer is able to deserialize.
146      */
147     public val descriptor: SerialDescriptor
148 
149     /**
150      * Deserializes the value of type [T] using the format that is represented by the given [decoder].
151      * [deserialize] method is format-agnostic and operates with a high-level structured [Decoder] API.
152      * As long as most of the formats imply an arbitrary order of properties, deserializer should be able
153      * to decode these properties in an arbitrary order and in a format-agnostic way.
154      * For that purposes, [CompositeDecoder.decodeElementIndex]-based loop is used: decoder firstly
155      * signals property at which index it is ready to decode and then expects caller to decode
156      * property with the given index.
157      *
158      * Throws [SerializationException] if value cannot be deserialized.
159      *
160      * Example of deserialize method:
161      * ```
162      * class MyData(int: Int, stringList: List<String>, alwaysZero: Long)
163      *
164      * fun deserialize(decoder: Decoder): MyData = decoder.decodeStructure(descriptor) {
165      *     // decodeStructure decodes beginning and end of the structure
166      *     var int: Int? = null
167      *     var list: List<String>? = null
168      *     loop@ while (true) {
169      *         when (val index = decodeElementIndex(descriptor)) {
170      *             DECODE_DONE -> break@loop
171      *             0 -> {
172      *                 // Decode 'int' property as Int
173      *                 int = decodeIntElement(descriptor, index = 0)
174      *             }
175      *             1 -> {
176      *                 // Decode 'stringList' property as List<String>
177      *                 list = decodeSerializableElement(descriptor, index = 1, serializer<List<String>>())
178      *             }
179      *             else -> throw SerializationException("Unexpected index $index")
180      *         }
181      *      }
182      *     if (int == null || list == null) throwMissingFieldException()
183      *     // Always use 0 as a value for alwaysZero property because we decided to do so.
184      *     return MyData(int, list, alwaysZero = 0L)
185      * }
186      * ```
187      *
188      * @throws MissingFieldException if non-optional fields were not found during deserialization
189      * @throws SerializationException in case of any deserialization-specific error
190      * @throws IllegalArgumentException if the decoded input is not a valid instance of [T]
191      * @see KSerializer for additional information about general contracts and exception specifics
192      */
193     public fun deserialize(decoder: Decoder): T
194 }
195