Skip to content

Commit 0f1034e

Browse files
authored
Update value classes guide (#1973)
1 parent 621dbf8 commit 0f1034e

File tree

5 files changed

+211
-212
lines changed

5 files changed

+211
-212
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ Using 1.1.0-RC, you can mark inline classes as `@Serializable` and use them in o
297297
Unsigned integer types (`UByte`, `UShort`, `UInt` and `ULong`) are serializable as well and have special support in JSON.
298298
This feature requires Kotlin compiler 1.4.30-RC and enabling new IR compilers for [JS](https://kotlinlang.org/docs/reference/js-ir-compiler.html) and [JVM](https://kotlinlang.org/docs/reference/whatsnew14.html#new-jvm-ir-backend).
299299

300-
You can learn more in the [documentation](docs/inline-classes.md)
300+
You can learn more in the [documentation](docs/value-classes.md)
301301
and corresponding [pull request](https://github.com/Kotlin/kotlinx.serialization/pull/1244).
302302

303303
### Other features

docs/inline-classes.md

Lines changed: 1 addition & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -1,205 +1 @@
1-
# Serialization and inline classes (experimental, IR-specific)
2-
3-
This appendix describes how inline classes are handled by kotlinx.serialization.
4-
5-
> Features described in this document are currently [experimental](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/compatibility.md#experimental-api)
6-
> and are available only with IR compilers. Native targets use IR compiler by default;
7-
> see documentation for [JS](https://kotlinlang.org/docs/reference/js-ir-compiler.html) and [JVM](https://kotlinlang.org/docs/reference/whatsnew14.html#new-jvm-ir-backend) to learn how to enable IR compilers.
8-
> Inline classes themselves are an [Alpha](https://kotlinlang.org/docs/reference/inline-classes.html#alpha-status-of-inline-classes) Kotlin feature.
9-
10-
**Table of contents**
11-
12-
<!--- TOC -->
13-
14-
* [Serializable inline classes](#serializable-inline-classes)
15-
* [Unsigned types support (JSON only)](#unsigned-types-support-json-only)
16-
* [Using inline classes in your custom serializers](#using-inline-classes-in-your-custom-serializers)
17-
18-
<!--- END -->
19-
20-
## Serializable inline classes
21-
22-
We can mark inline class as serializable:
23-
24-
```kotlin
25-
@Serializable
26-
inline class Color(val rgb: Int)
27-
```
28-
29-
Inline class in Kotlin is stored as its underlying type when possible (i.e. no boxing is required).
30-
Serialization framework makes does not impose any additional restriction and uses the underlying type where possible as well.
31-
32-
```kotlin
33-
@Serializable
34-
data class NamedColor(val color: Color, val name: String)
35-
36-
fun main() {
37-
println(Json.encodeToString(NamedColor(Color(0), "black")))
38-
}
39-
```
40-
41-
In this example, `NamedColor` is serialized as two primitives: `color: Int` and `name: String` without an allocation
42-
of `Color` class. When we run the example, encoding data with JSON format, we get the following
43-
output:
44-
45-
```text
46-
{"color": 0, "name": "black"}
47-
```
48-
49-
As we see, `Color` class is not included during the encoding, only its underlying data. This invariant holds even if the actual inline class
50-
is [allocated](https://kotlinlang.org/docs/reference/inline-classes.html#representation) — for example, when inline
51-
class is used as a generic type argument:
52-
53-
```kotlin
54-
@Serializable
55-
class Palette(val colors: List<Color>)
56-
57-
fun main() {
58-
println(Json.encodeToString(Palette(listOf(Color(0), Color(255), Color(128)))))
59-
}
60-
```
61-
62-
The snippet produces the following output:
63-
64-
```text
65-
{"colors":[0, 255, 128]}
66-
```
67-
68-
## Unsigned types support (JSON only)
69-
70-
Kotlin standard library provides ready-to-use unsigned arithmetics, leveraging inline classes
71-
to represent unsigned types: `UByte`, `UShort`, `UInt` and `ULong`.
72-
[Json] format has built-in support for them: these types are serialized as theirs string
73-
representations in unsigned form.
74-
These types are handled as regular serializable types by the compiler plugin and can be freely used in serializable classes:
75-
76-
```kotlin
77-
@Serializable
78-
class Counter(val counted: UByte, val description: String)
79-
80-
fun main() {
81-
val counted = 239.toUByte()
82-
println(Json.encodeToString(Counter(counted, "tries")))
83-
}
84-
```
85-
86-
The output is following:
87-
88-
```text
89-
{"counted":239,"description":"tries"}
90-
```
91-
92-
> Unsigned types are currently unsupported in Protobuf and CBOR, but we plan to add them later.
93-
94-
## Using inline classes in your custom serializers
95-
96-
Let's return to our `NamedColor` example and try to write a custom serializer for it. Normally, as shown
97-
in [Hand-written composite serializer](serializers.md#hand-written-composite-serializer), we would write the following code
98-
in `serialize` method:
99-
100-
```kotlin
101-
override fun serialize(encoder: Encoder, value: NamedColor) {
102-
encoder.beginStructure(descriptor) {
103-
encodeSerializableElement(descriptor, 0, Color.serializer(), value.color)
104-
encodeStringElement(descriptor, 1, value.name)
105-
}
106-
}
107-
```
108-
109-
However, since `Color` is used as a type argument in [encodeSerializableElement][CompositeEncoder.encodeSerializableElement] function, `value.color` will be boxed
110-
to `Color` wrapper before passing it to the function, preventing the inline class optimization. To avoid this, we can use
111-
special [encodeInlineElement][CompositeEncoder.encodeInlineElement] function instead. It uses [serial descriptor][SerialDescriptor] of `Color` ([retrieved][SerialDescriptor.getElementDescriptor] from serial descriptor of `NamedColor`) instead of [KSerializer],
112-
does not have type parameters and does not accept any values. Instead, it returns [Encoder]. Using it, we can encode
113-
unboxed value:
114-
115-
```kotlin
116-
override fun serialize(encoder: Encoder, value: NamedColor) {
117-
encoder.beginStructure(descriptor) {
118-
encodeInlineElement(descriptor, 0).encodeInt(value.color)
119-
encodeStringElement(descriptor, 1, value.name)
120-
}
121-
}
122-
```
123-
124-
The same principle goes also with [CompositeDecoder]: it has [decodeInlineElement][CompositeDecoder.decodeInlineElement] function that returns [Decoder].
125-
126-
If your class should be represented as a primitive (as shown in [Primitive serializer](serializers.md#primitive-serializer) section),
127-
and you cannot use [beginStructure][Encoder.beginStructure] function, there is a complementary function in [Encoder] called [encodeInline][Encoder.encodeInline].
128-
We will use it to show an example how one can represent a class as an unsigned integer.
129-
130-
Let's start with a UID class:
131-
132-
```kotlin
133-
@Serializable(UIDSerializer::class)
134-
class UID(val uid: Int)
135-
```
136-
137-
`uid` type is `Int`, but suppose we want it to be an unsigned integer in JSON. We can start writing the
138-
following custom serializer:
139-
140-
```kotlin
141-
object UIDSerializer: KSerializer<UID> {
142-
override val descriptor = UInt.serializer().descriptor
143-
}
144-
```
145-
146-
Note that we are using here descriptor from `UInt.serializer()` — it means that the class' representation looks like a
147-
UInt's one.
148-
149-
Then the `serialize` method:
150-
151-
```kotlin
152-
override fun serialize(encoder: Encoder, value: UID) {
153-
encoder.encodeInline(descriptor).encodeInt(value.uid)
154-
}
155-
```
156-
157-
That's where the magic happens — despite we called a regular [encodeInt][Encoder.encodeInt] with a `uid: Int` argument, the output will contain
158-
an unsigned int because of the special encoder from `encodeInline` function. Since JSON format supports unsigned integers, it
159-
recognizes theirs descriptors when they're passed into `encodeInline` and handles consecutive calls as for unsigned integers.
160-
161-
The `deserialize` method looks symmetrically:
162-
163-
```kotlin
164-
override fun deserialize(decoder: Decoder): UID {
165-
return UID(decoder.decodeInline(descriptor).decodeInt())
166-
}
167-
```
168-
169-
> Disclaimer: You can also write such a serializer for inline class itself (imagine UID being the inline class — there's no need to change anything in the serializer).
170-
> However, do not use anything in custom serializers for inline classes besides `encodeInline`. As we discussed, calls to inline class serializer may be
171-
> optimized and replaced with a `encodeInlineElement` calls.
172-
> `encodeInline` and `encodeInlineElement` calls with the same descriptor are considered equivalent and can be replaced with each other — formats should return the same `Encoder`.
173-
> If you embed custom logic in custom inline class serializer, you may get different results depending on whether this serializer was called at all
174-
> (and this, in turn, depends on whether inline class was boxed or not).
175-
176-
---
177-
178-
<!--- MODULE /kotlinx-serialization-core -->
179-
<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
180-
181-
[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
182-
183-
<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
184-
185-
[CompositeEncoder.encodeSerializableElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-serializable-element.html
186-
[CompositeEncoder.encodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-inline-element.html
187-
[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
188-
[CompositeDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/index.html
189-
[CompositeDecoder.decodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-inline-element.html
190-
[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
191-
[Encoder.beginStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/begin-structure.html
192-
[Encoder.encodeInline]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-inline.html
193-
[Encoder.encodeInt]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-int.html
194-
195-
<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.descriptors -->
196-
197-
[SerialDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/index.html
198-
[SerialDescriptor.getElementDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/get-element-descriptor.html
199-
200-
<!--- MODULE /kotlinx-serialization-json -->
201-
<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
202-
203-
[Json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
204-
205-
<!--- END -->
1+
The documentation has been moved to the [value-classes.md](value-classes.md) page.

docs/serialization-guide.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,10 @@ Once the project is set up, we can start serializing some classes.
153153
* <a name='format-specific-types'></a>[Format-specific types](formats.md#format-specific-types)
154154
<!--- END -->
155155

156-
**Appendix A.** [Serialization and inline classes (experimental, IR-specific)](inline-classes.md)
156+
**Appendix A.** [Serialization and value classes (IR-only)](value-classes.md)
157157

158-
<!--- TOC_REF inline-classes.md -->
159-
* <a name='serializable-inline-classes'></a>[Serializable inline classes](inline-classes.md#serializable-inline-classes)
160-
* <a name='unsigned-types-support-json-only'></a>[Unsigned types support (JSON only)](inline-classes.md#unsigned-types-support-json-only)
161-
* <a name='using-inline-classes-in-your-custom-serializers'></a>[Using inline classes in your custom serializers](inline-classes.md#using-inline-classes-in-your-custom-serializers)
158+
<!--- TOC_REF value-classes.md -->
159+
* <a name='serializable-value-classes'></a>[Serializable value classes](value-classes.md#serializable-value-classes)
160+
* <a name='unsigned-types-support-json-only'></a>[Unsigned types support (JSON only)](value-classes.md#unsigned-types-support-json-only)
161+
* <a name='using-value-classes-in-your-custom-serializers'></a>[Using value classes in your custom serializers](value-classes.md#using-value-classes-in-your-custom-serializers)
162162
<!--- END -->

0 commit comments

Comments
 (0)