Skip to content

Commit 1c223b8

Browse files
authored
Refine SerialDescriptor documentation (#2144)
1 parent 04e0983 commit 1c223b8

File tree

1 file changed

+27
-13
lines changed

1 file changed

+27
-13
lines changed

core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
package kotlinx.serialization.descriptors
66

77
import kotlinx.serialization.*
8+
import kotlinx.serialization.builtins.*
89
import kotlinx.serialization.encoding.*
910

1011
/**
1112
* Serial descriptor is an inherent property of [KSerializer] that describes the structure of the serializable type.
12-
* The structure of the serializable type is not only the property of the type, but also of the serializer as well,
13+
* The structure of the serializable type is not only the characteristic of the type itself, but also of the serializer as well,
1314
* meaning that one type can have multiple descriptors that have completely different structure.
1415
*
1516
* For example, the class `class Color(val rgb: Int)` can have multiple serializable representations,
@@ -25,19 +26,19 @@ import kotlinx.serialization.encoding.*
2526
* For generic types, the actual type substitution is omitted from the string representation and the name
2627
* identifies the family of the serializers without type substitutions. However, type substitution is accounted
2728
* in [equals] and [hashCode] operations, meaning that descriptors of generic classes with the same name, but different type
28-
* parameters, are not equal to each other.
29+
* arguments, are not equal to each other.
2930
* [serialName] is typically used to specify the type of the target class during serialization of polymorphic and sealed
3031
* classes, for observability and diagnostics.
31-
* * [Kind][SerialKind] defines what this descriptor represents: primitive, enum, object, collection et cetera.
32+
* * [Kind][SerialKind] defines what this descriptor represents: primitive, enum, object, collection etc.
3233
* * Children elements are represented as serial descriptors as well and define the structure of the type's elements.
33-
* * Metadata carries additional potentially useful information, such as [nullability][nullable], [optionality][isElementOptional]
34+
* * Metadata carries additional information, such as [nullability][nullable], [optionality][isElementOptional]
3435
* and [serial annotations][getElementAnnotations].
3536
*
3637
* ### Usages
3738
* There are two general usages of the descriptors: THE serialization process and serialization introspection.
3839
*
3940
* #### Serialization
40-
* Serial descriptor is used as bridge between decoders/encoders and serializers.
41+
* Serial descriptor is used as a bridge between decoders/encoders and serializers.
4142
* When asking for a next element, the serializer provides an expected descriptor to the decoder, and,
4243
* based on the descriptor content, decoder decides how to parse its input.
4344
* In JSON, for example, when the encoder is asked to encode the next element and this element
@@ -59,15 +60,15 @@ import kotlinx.serialization.encoding.*
5960
* the range from zero to [elementsCount] and represent and index of the property in this class.
6061
* Consequently, primitives do not have children and their element count is zero.
6162
*
62-
* For collections and maps, though, indices does not have fixed bound. Regular collections descriptors usually
63+
* For collections and maps indices don't have fixed bound. Regular collections descriptors usually
6364
* have one element (`T`, maps have two, one for keys and one for values), but potentially unlimited
6465
* number of actual children values. Valid indices range is not known statically
65-
* and implementations of descriptor should provide consistent and unbounded names and indices.
66+
* and implementations of such descriptor should provide consistent and unbounded names and indices.
6667
*
6768
* In practice, for regular classes it is allowed to invoke `getElement*(index)` methods
68-
* with an index within `0 until elementsCount` range and element at the particular index corresponds to the
69+
* with an index from `0` to [elementsCount] range and element at the particular index corresponds to the
6970
* serializable property at the given position.
70-
* For collections and maps, index parameter for `getElement*(index)` methods is effectively bound
71+
* For collections and maps, index parameter for `getElement*(index)` methods is effectively bounded
7172
* by the maximal number of collection/map elements.
7273
*
7374
* ### Thread-safety and mutability
@@ -76,7 +77,6 @@ import kotlinx.serialization.encoding.*
7677
* ### Equality and caching
7778
* Serial descriptor can be used as a unique identifier for format-specific data or schemas and
7879
* this implies the following restrictions on its `equals` and `hashCode`:
79-
* *
8080
*
8181
* An [equals] implementation should use both [serialName] and elements structure.
8282
* Comparing [elementDescriptors] directly is discouraged,
@@ -100,8 +100,8 @@ import kotlinx.serialization.encoding.*
100100
* [hashCode] implementation should use the same properties for computing the result.
101101
*
102102
* ### User-defined serial descriptors
103-
* The best way to define a custom descriptor is to use [SerialDescriptor] builder function, where
104-
* for each serializable property corresponding element is declared.
103+
* The best way to define a custom descriptor is to use [buildClassSerialDescriptor] builder function, where
104+
* for each serializable property the corresponding element is declared.
105105
*
106106
* Example:
107107
* ```
@@ -113,15 +113,29 @@ import kotlinx.serialization.encoding.*
113113
* )
114114
*
115115
* // Descriptor for such class:
116-
* SerialDescriptor("my.package.Data") {
116+
* buildClassSerialDescriptor("my.package.Data") {
117117
* // intField is deliberately ignored by serializer -- not present in the descriptor as well
118118
* element<Long>("_longField") // longField is named as _longField
119119
* element("stringField", listSerialDescriptor<String>())
120120
* }
121+
*
122+
* // Example of 'serialize' function for such descriptor
123+
* override fun serialize(encoder: Encoder, value: Data) {
124+
* encoder.encodeStructure(descriptor) {
125+
* encodeLongElement(descriptor, 0, value.longField) // Will be written as "_longField" because descriptor's child at index 0 says so
126+
* encodeSerializableElement(descriptor, 1, ListSerializer(String.serializer()), value.stringList)
127+
* }
128+
* }
121129
* ```
122130
*
123131
* For a classes that are represented as a single primitive value, [PrimitiveSerialDescriptor] builder function can be used instead.
124132
*
133+
* ### Consistency violations
134+
* An implementation of [SerialDescriptor] should be consistent with the implementation of the corresponding [KSerializer].
135+
* Yet it is not type-checked statically, thus making it possible to declare a non-consistent implementations of descriptor and serializer.
136+
* In such cases, the behaviour of an underlying format is unspecified and may lead to both runtime errors and encoding of
137+
* corrupted data that is impossible to decode back.
138+
*
125139
* ### Not stable for inheritance
126140
*
127141
* `SerialDescriptor` interface is not stable for inheritance in 3rd party libraries, as new methods

0 commit comments

Comments
 (0)