summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVsevolod Tolstopyatov <qwwdfsad@gmail.com>2022-07-04 12:17:24 +0200
committerGitHub <noreply@github.com>2022-07-04 13:17:24 +0300
commit0f1034e670a6581cdba54e4aa5cb1257e5884ea9 (patch)
tree33460ec178a905fa3c77ee874a3241eb4ff1ea39
parent621dbf8992e8ff508e8a265e4733d7893a9aeab8 (diff)
downloadkotlinx.serialization-0f1034e670a6581cdba54e4aa5cb1257e5884ea9.tar.gz
Update value classes guide (#1973)
-rw-r--r--CHANGELOG.md2
-rw-r--r--docs/inline-classes.md206
-rw-r--r--docs/serialization-guide.md10
-rw-r--r--docs/value-classes.md204
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/Json.kt1
5 files changed, 211 insertions, 212 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 621bf09e..2025e1a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -297,7 +297,7 @@ Using 1.1.0-RC, you can mark inline classes as `@Serializable` and use them in o
Unsigned integer types (`UByte`, `UShort`, `UInt` and `ULong`) are serializable as well and have special support in JSON.
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).
-You can learn more in the [documentation](docs/inline-classes.md)
+You can learn more in the [documentation](docs/value-classes.md)
and corresponding [pull request](https://github.com/Kotlin/kotlinx.serialization/pull/1244).
### Other features
diff --git a/docs/inline-classes.md b/docs/inline-classes.md
index a28f51c0..fda81420 100644
--- a/docs/inline-classes.md
+++ b/docs/inline-classes.md
@@ -1,205 +1 @@
-# Serialization and inline classes (experimental, IR-specific)
-
-This appendix describes how inline classes are handled by kotlinx.serialization.
-
-> Features described in this document are currently [experimental](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/compatibility.md#experimental-api)
-> and are available only with IR compilers. Native targets use IR compiler by default;
-> 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.
-> Inline classes themselves are an [Alpha](https://kotlinlang.org/docs/reference/inline-classes.html#alpha-status-of-inline-classes) Kotlin feature.
-
-**Table of contents**
-
-<!--- TOC -->
-
-* [Serializable inline classes](#serializable-inline-classes)
-* [Unsigned types support (JSON only)](#unsigned-types-support-json-only)
-* [Using inline classes in your custom serializers](#using-inline-classes-in-your-custom-serializers)
-
-<!--- END -->
-
-## Serializable inline classes
-
-We can mark inline class as serializable:
-
-```kotlin
-@Serializable
-inline class Color(val rgb: Int)
-```
-
-Inline class in Kotlin is stored as its underlying type when possible (i.e. no boxing is required).
-Serialization framework makes does not impose any additional restriction and uses the underlying type where possible as well.
-
-```kotlin
-@Serializable
-data class NamedColor(val color: Color, val name: String)
-
-fun main() {
- println(Json.encodeToString(NamedColor(Color(0), "black")))
-}
-```
-
-In this example, `NamedColor` is serialized as two primitives: `color: Int` and `name: String` without an allocation
-of `Color` class. When we run the example, encoding data with JSON format, we get the following
-output:
-
-```text
-{"color": 0, "name": "black"}
-```
-
-As we see, `Color` class is not included during the encoding, only its underlying data. This invariant holds even if the actual inline class
-is [allocated](https://kotlinlang.org/docs/reference/inline-classes.html#representation) — for example, when inline
-class is used as a generic type argument:
-
-```kotlin
-@Serializable
-class Palette(val colors: List<Color>)
-
-fun main() {
- println(Json.encodeToString(Palette(listOf(Color(0), Color(255), Color(128)))))
-}
-```
-
-The snippet produces the following output:
-
-```text
-{"colors":[0, 255, 128]}
-```
-
-## Unsigned types support (JSON only)
-
-Kotlin standard library provides ready-to-use unsigned arithmetics, leveraging inline classes
-to represent unsigned types: `UByte`, `UShort`, `UInt` and `ULong`.
-[Json] format has built-in support for them: these types are serialized as theirs string
-representations in unsigned form.
-These types are handled as regular serializable types by the compiler plugin and can be freely used in serializable classes:
-
-```kotlin
-@Serializable
-class Counter(val counted: UByte, val description: String)
-
-fun main() {
- val counted = 239.toUByte()
- println(Json.encodeToString(Counter(counted, "tries")))
-}
-```
-
-The output is following:
-
-```text
-{"counted":239,"description":"tries"}
-```
-
-> Unsigned types are currently unsupported in Protobuf and CBOR, but we plan to add them later.
-
-## Using inline classes in your custom serializers
-
-Let's return to our `NamedColor` example and try to write a custom serializer for it. Normally, as shown
-in [Hand-written composite serializer](serializers.md#hand-written-composite-serializer), we would write the following code
-in `serialize` method:
-
-```kotlin
-override fun serialize(encoder: Encoder, value: NamedColor) {
- encoder.beginStructure(descriptor) {
- encodeSerializableElement(descriptor, 0, Color.serializer(), value.color)
- encodeStringElement(descriptor, 1, value.name)
- }
-}
-```
-
-However, since `Color` is used as a type argument in [encodeSerializableElement][CompositeEncoder.encodeSerializableElement] function, `value.color` will be boxed
-to `Color` wrapper before passing it to the function, preventing the inline class optimization. To avoid this, we can use
-special [encodeInlineElement][CompositeEncoder.encodeInlineElement] function instead. It uses [serial descriptor][SerialDescriptor] of `Color` ([retrieved][SerialDescriptor.getElementDescriptor] from serial descriptor of `NamedColor`) instead of [KSerializer],
-does not have type parameters and does not accept any values. Instead, it returns [Encoder]. Using it, we can encode
-unboxed value:
-
-```kotlin
-override fun serialize(encoder: Encoder, value: NamedColor) {
- encoder.beginStructure(descriptor) {
- encodeInlineElement(descriptor, 0).encodeInt(value.color)
- encodeStringElement(descriptor, 1, value.name)
- }
-}
-```
-
-The same principle goes also with [CompositeDecoder]: it has [decodeInlineElement][CompositeDecoder.decodeInlineElement] function that returns [Decoder].
-
-If your class should be represented as a primitive (as shown in [Primitive serializer](serializers.md#primitive-serializer) section),
-and you cannot use [beginStructure][Encoder.beginStructure] function, there is a complementary function in [Encoder] called [encodeInline][Encoder.encodeInline].
-We will use it to show an example how one can represent a class as an unsigned integer.
-
-Let's start with a UID class:
-
-```kotlin
-@Serializable(UIDSerializer::class)
-class UID(val uid: Int)
-```
-
-`uid` type is `Int`, but suppose we want it to be an unsigned integer in JSON. We can start writing the
-following custom serializer:
-
-```kotlin
-object UIDSerializer: KSerializer<UID> {
- override val descriptor = UInt.serializer().descriptor
-}
-```
-
-Note that we are using here descriptor from `UInt.serializer()` — it means that the class' representation looks like a
-UInt's one.
-
-Then the `serialize` method:
-
-```kotlin
-override fun serialize(encoder: Encoder, value: UID) {
- encoder.encodeInline(descriptor).encodeInt(value.uid)
-}
-```
-
-That's where the magic happens — despite we called a regular [encodeInt][Encoder.encodeInt] with a `uid: Int` argument, the output will contain
-an unsigned int because of the special encoder from `encodeInline` function. Since JSON format supports unsigned integers, it
-recognizes theirs descriptors when they're passed into `encodeInline` and handles consecutive calls as for unsigned integers.
-
-The `deserialize` method looks symmetrically:
-
-```kotlin
-override fun deserialize(decoder: Decoder): UID {
- return UID(decoder.decodeInline(descriptor).decodeInt())
-}
-```
-
-> 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).
-> However, do not use anything in custom serializers for inline classes besides `encodeInline`. As we discussed, calls to inline class serializer may be
-> optimized and replaced with a `encodeInlineElement` calls.
-> `encodeInline` and `encodeInlineElement` calls with the same descriptor are considered equivalent and can be replaced with each other — formats should return the same `Encoder`.
-> If you embed custom logic in custom inline class serializer, you may get different results depending on whether this serializer was called at all
-> (and this, in turn, depends on whether inline class was boxed or not).
-
----
-
-<!--- MODULE /kotlinx-serialization-core -->
-<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
-
-[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
-
-<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
-
-[CompositeEncoder.encodeSerializableElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-serializable-element.html
-[CompositeEncoder.encodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-inline-element.html
-[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
-[CompositeDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/index.html
-[CompositeDecoder.decodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-inline-element.html
-[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
-[Encoder.beginStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/begin-structure.html
-[Encoder.encodeInline]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-inline.html
-[Encoder.encodeInt]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-int.html
-
-<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.descriptors -->
-
-[SerialDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/index.html
-[SerialDescriptor.getElementDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/get-element-descriptor.html
-
-<!--- MODULE /kotlinx-serialization-json -->
-<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
-
-[Json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
-
-<!--- END -->
+The documentation has been moved to the [value-classes.md](value-classes.md) page.
diff --git a/docs/serialization-guide.md b/docs/serialization-guide.md
index e3f28eae..e82e4dcc 100644
--- a/docs/serialization-guide.md
+++ b/docs/serialization-guide.md
@@ -153,10 +153,10 @@ Once the project is set up, we can start serializing some classes.
* <a name='format-specific-types'></a>[Format-specific types](formats.md#format-specific-types)
<!--- END -->
-**Appendix A.** [Serialization and inline classes (experimental, IR-specific)](inline-classes.md)
+**Appendix A.** [Serialization and value classes (IR-only)](value-classes.md)
-<!--- TOC_REF inline-classes.md -->
-* <a name='serializable-inline-classes'></a>[Serializable inline classes](inline-classes.md#serializable-inline-classes)
-* <a name='unsigned-types-support-json-only'></a>[Unsigned types support (JSON only)](inline-classes.md#unsigned-types-support-json-only)
-* <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)
+<!--- TOC_REF value-classes.md -->
+* <a name='serializable-value-classes'></a>[Serializable value classes](value-classes.md#serializable-value-classes)
+* <a name='unsigned-types-support-json-only'></a>[Unsigned types support (JSON only)](value-classes.md#unsigned-types-support-json-only)
+* <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)
<!--- END -->
diff --git a/docs/value-classes.md b/docs/value-classes.md
new file mode 100644
index 00000000..476b5db0
--- /dev/null
+++ b/docs/value-classes.md
@@ -0,0 +1,204 @@
+# Serialization and value classes (IR-specific)
+
+This appendix describes how value classes are handled by kotlinx.serialization.
+
+> Features described are available on JVM/IR (enabled by default), JS/IR and Native backends.
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Serializable value classes](#serializable-value-classes)
+* [Unsigned types support (JSON only)](#unsigned-types-support-json-only)
+* [Using value classes in your custom serializers](#using-value-classes-in-your-custom-serializers)
+
+<!--- END -->
+
+## Serializable value classes
+
+We can mark value class as serializable:
+
+```kotlin
+@Serializable
+@JvmInline
+value class Color(val rgb: Int)
+```
+
+Value class in Kotlin is stored as its underlying type when possible (i.e. no boxing is required).
+Serialization framework does not impose any additional restrictions and uses the underlying type where possible as well.
+
+```kotlin
+@Serializable
+data class NamedColor(val color: Color, val name: String)
+
+fun main() {
+ println(Json.encodeToString(NamedColor(Color(0), "black")))
+}
+```
+
+In this example, `NamedColor` is serialized as two primitives: `color: Int` and `name: String` without an allocation
+of `Color` class. When we run the example, encoding data with JSON format, we get the following
+output:
+
+```text
+{"color": 0, "name": "black"}
+```
+
+As we see, `Color` class is not included during the encoding, only its underlying data. This invariant holds even if the actual value class
+is [allocated](https://kotlinlang.org/docs/reference/inline-classes.html#representation) — for example, when inline
+class is used as a generic type argument:
+
+```kotlin
+@Serializable
+class Palette(val colors: List<Color>)
+
+fun main() {
+ println(Json.encodeToString(Palette(listOf(Color(0), Color(255), Color(128)))))
+}
+```
+
+The snippet produces the following output:
+
+```text
+{"colors":[0, 255, 128]}
+```
+
+## Unsigned types support (JSON only)
+
+Kotlin standard library provides ready-to-use unsigned arithmetics, leveraging value classes
+to represent unsigned types: `UByte`, `UShort`, `UInt` and `ULong`.
+[Json] format has built-in support for them: these types are serialized as theirs string
+representations in unsigned form.
+These types are handled as regular serializable types by the compiler plugin and can be freely used in serializable classes:
+
+```kotlin
+@Serializable
+class Counter(val counted: UByte, val description: String)
+
+fun main() {
+ val counted = 239.toUByte()
+ println(Json.encodeToString(Counter(counted, "tries")))
+}
+```
+
+The output is following:
+
+```text
+{"counted":239,"description":"tries"}
+```
+
+> Unsigned types are currently supported only in JSON format. Other formats such as ProtoBuf and CBOR
+> use built-in serializers that use an underlying signed representation for unsigned types.
+
+## Using value classes in your custom serializers
+
+Let's return to our `NamedColor` example and try to write a custom serializer for it. Normally, as shown
+in [Hand-written composite serializer](serializers.md#hand-written-composite-serializer), we would write the following code
+in `serialize` method:
+
+```kotlin
+override fun serialize(encoder: Encoder, value: NamedColor) {
+ encoder.beginStructure(descriptor) {
+ encodeSerializableElement(descriptor, 0, Color.serializer(), value.color)
+ encodeStringElement(descriptor, 1, value.name)
+ }
+}
+```
+
+However, since `Color` is used as a type argument in [encodeSerializableElement][CompositeEncoder.encodeSerializableElement] function, `value.color` will be boxed
+to `Color` wrapper before passing it to the function, preventing the value class optimization. To avoid this, we can use
+special [encodeInlineElement][CompositeEncoder.encodeInlineElement] function instead. It uses [serial descriptor][SerialDescriptor] of `Color` ([retrieved][SerialDescriptor.getElementDescriptor] from serial descriptor of `NamedColor`) instead of [KSerializer],
+does not have type parameters and does not accept any values. Instead, it returns [Encoder]. Using it, we can encode
+unboxed value:
+
+```kotlin
+override fun serialize(encoder: Encoder, value: NamedColor) {
+ encoder.beginStructure(descriptor) {
+ encodeInlineElement(descriptor, 0).encodeInt(value.color)
+ encodeStringElement(descriptor, 1, value.name)
+ }
+}
+```
+
+The same principle goes also with [CompositeDecoder]: it has [decodeInlineElement][CompositeDecoder.decodeInlineElement] function that returns [Decoder].
+
+If your class should be represented as a primitive (as shown in [Primitive serializer](serializers.md#primitive-serializer) section),
+and you cannot use [beginStructure][Encoder.beginStructure] function, there is a complementary function in [Encoder] called [encodeInline][Encoder.encodeInline].
+We will use it to show an example how one can represent a class as an unsigned integer.
+
+Let's start with a UID class:
+
+```kotlin
+@Serializable(UIDSerializer::class)
+class UID(val uid: Int)
+```
+
+`uid` type is `Int`, but suppose we want it to be an unsigned integer in JSON. We can start writing the
+following custom serializer:
+
+```kotlin
+object UIDSerializer: KSerializer<UID> {
+ override val descriptor = UInt.serializer().descriptor
+}
+```
+
+Note that we are using here descriptor from `UInt.serializer()` — it means that the class' representation looks like a
+UInt's one.
+
+Then the `serialize` method:
+
+```kotlin
+override fun serialize(encoder: Encoder, value: UID) {
+ encoder.encodeInline(descriptor).encodeInt(value.uid)
+}
+```
+
+That's where the magic happens — despite we called a regular [encodeInt][Encoder.encodeInt] with a `uid: Int` argument, the output will contain
+an unsigned int because of the special encoder from `encodeInline` function. Since JSON format supports unsigned integers, it
+recognizes theirs descriptors when they're passed into `encodeInline` and handles consecutive calls as for unsigned integers.
+
+The `deserialize` method looks symmetrically:
+
+```kotlin
+override fun deserialize(decoder: Decoder): UID {
+ return UID(decoder.decodeInline(descriptor).decodeInt())
+}
+```
+
+> Disclaimer: You can also write such a serializer for value class itself (imagine UID being the value class — there's no need to change anything in the serializer).
+> However, do not use anything in custom serializers for value classes besides `encodeInline`. As we discussed, calls to value class serializer may be
+> optimized and replaced with a `encodeInlineElement` calls.
+> `encodeInline` and `encodeInlineElement` calls with the same descriptor are considered equivalent and can be replaced with each other — formats should return the same `Encoder`.
+> If you embed custom logic in custom value class serializer, you may get different results depending on whether this serializer was called at all
+> (and this, in turn, depends on whether value class was boxed or not).
+
+---
+
+<!--- MODULE /kotlinx-serialization-core -->
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
+
+[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
+
+[CompositeEncoder.encodeSerializableElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-serializable-element.html
+[CompositeEncoder.encodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/encode-inline-element.html
+[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
+[CompositeDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/index.html
+[CompositeDecoder.decodeInlineElement]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-inline-element.html
+[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
+[Encoder.beginStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/begin-structure.html
+[Encoder.encodeInline]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-inline.html
+[Encoder.encodeInt]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-int.html
+
+<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.descriptors -->
+
+[SerialDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/index.html
+[SerialDescriptor.getElementDescriptor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/get-element-descriptor.html
+
+<!--- MODULE /kotlinx-serialization-json -->
+<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
+
+[Json]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
+
+<!--- END -->
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
index abb9b4bc..04871511 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
@@ -50,7 +50,6 @@ import kotlin.native.concurrent.*
* Json instance also exposes its [configuration] that can be used in custom serializers
* that rely on [JsonDecoder] and [JsonEncoder] for customizable behaviour.
*/
-@OptIn(ExperimentalSerializationApi::class)
public sealed class Json(
public val configuration: JsonConfiguration,
override val serializersModule: SerializersModule