Skip to content

Commit 71ddbfd

Browse files
mongodbencbullingerrustagir
authored
(DOCSP-29226): KotlinX Serialization (#48)
# Pull Request Info [PR Reviewing Guidelines](https://github.com/mongodb/docs-java/blob/master/REVIEWING.md) JIRA - https://jira.mongodb.org/browse/DOCSP-29226 Staging - https://docs-mongodbcom-staging.corp.mongodb.com/kotlin/docsworker-xlarge/DOCSP-29226/fundamentals/data-formats/serialization/ ## Self-Review Checklist - [ ] Is this free of any warnings or errors in the RST? - [ ] Did you run a spell-check? - [ ] Did you run a grammar-check? - [ ] Are all the links working? --------- Co-authored-by: cbullinger <[email protected]> Co-authored-by: Rea Rustagi <[email protected]>
1 parent 8ebf256 commit 71ddbfd

File tree

8 files changed

+335
-5
lines changed

8 files changed

+335
-5
lines changed

examples/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44
kotlin("jvm") version "1.8.0"
55
id("com.google.osdetector") version "1.7.3"
66
application
7+
kotlin("plugin.serialization") version "1.8.21"
78
}
89

910
group = "org.mongodb.docs.kotlin"
@@ -27,6 +28,9 @@ dependencies {
2728
implementation("io.netty:netty-tcnative-boringssl-static:2.0.59.Final:${osdetector.classifier}")
2829
implementation("org.xerial.snappy:snappy-java:1.1.10.0")
2930
implementation("com.github.luben:zstd-jni:1.5.5-4")
31+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
32+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
33+
implementation("org.mongodb:bson-kotlinx:4.10.0-alpha1")
3034
}
3135

3236
tasks.test {
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
2+
import com.mongodb.kotlin.client.coroutine.MongoClient
3+
import config.getConfig
4+
import kotlinx.coroutines.flow.first
5+
import kotlinx.coroutines.flow.firstOrNull
6+
import kotlinx.coroutines.runBlocking
7+
import kotlinx.serialization.Contextual
8+
import kotlinx.serialization.ExperimentalSerializationApi
9+
import kotlinx.serialization.SerialName
10+
import kotlinx.serialization.Serializable
11+
import kotlinx.serialization.json.Json
12+
import kotlinx.serialization.json.encodeToJsonElement
13+
import org.bson.Document
14+
import org.bson.codecs.configuration.CodecRegistries
15+
import org.bson.codecs.kotlinx.BsonConfiguration
16+
import org.bson.codecs.kotlinx.KotlinSerializerCodec
17+
import org.bson.codecs.kotlinx.ObjectIdSerializer
18+
import org.bson.types.ObjectId
19+
import org.junit.jupiter.api.AfterAll
20+
import org.junit.jupiter.api.Assertions.*
21+
import org.junit.jupiter.api.Test
22+
import org.junit.jupiter.api.TestInstance
23+
import java.util.*
24+
import kotlin.test.*
25+
26+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
27+
internal class KotlinXSerializationTest {
28+
29+
companion object {
30+
val config = getConfig()
31+
val client = MongoClient.create(config.connectionUri)
32+
val database = client.getDatabase("serialization")
33+
34+
@AfterAll
35+
@JvmStatic
36+
fun afterAll() {
37+
runBlocking {
38+
database.drop()
39+
client.close()
40+
}
41+
}
42+
}
43+
44+
45+
@Test
46+
fun basicEncodeToJsonTest() = runBlocking {
47+
@Serializable
48+
data class PaintOrder(
49+
@SerialName("_id")
50+
val id: Int,
51+
val color: String,
52+
val qty: Int
53+
)
54+
55+
val paintOrder = PaintOrder(1, "red", 5)
56+
val collection = database.getCollection<PaintOrder>("orders")
57+
58+
val insertOneResult = collection.insertOne(paintOrder)
59+
println(insertOneResult)
60+
println(Json.encodeToJsonElement(paintOrder)) // Only works if you don't have BSON properties (e.g. ObjectId)
61+
62+
assertEquals(paintOrder.id, 1)
63+
64+
collection.drop()
65+
}
66+
@Test
67+
fun basicSerializationTest() = runBlocking {
68+
// :snippet-start: basic-serialization
69+
@Serializable
70+
data class PaintOrder(
71+
@SerialName("_id") // Use instead of @BsonId
72+
@Contextual val id: ObjectId?,
73+
val color: String,
74+
val qty: Int,
75+
@SerialName("brand")
76+
val manufacturer: String = "Acme" // Use instead of @BsonProperty
77+
)
78+
// :snippet-end:
79+
80+
val collection = database.getCollection<PaintOrder>("orders")
81+
val paintOrder = PaintOrder(ObjectId(), "red", 5, "Acme")
82+
val insertOneResult = collection.insertOne(paintOrder)
83+
assertEquals(paintOrder.id, insertOneResult.insertedId?.asObjectId()?.value)
84+
85+
collection.drop()
86+
}
87+
88+
@OptIn(ExperimentalSerializationApi::class)
89+
@Test
90+
fun customSerializationTest() = runBlocking {
91+
@Serializable
92+
data class PaintOrder(
93+
@SerialName("_id")
94+
@Contextual val id: @Serializable(with = ObjectIdSerializer::class) ObjectId?,
95+
val color: String,
96+
val qty: Int,
97+
@SerialName("brand")
98+
val manufacturer: String = "Acme"
99+
)
100+
101+
val collection = database.getCollection<PaintOrder>("orders2")
102+
103+
// :snippet-start: custom-serialization
104+
val myCustomCodec = KotlinSerializerCodec.create<PaintOrder>(
105+
bsonConfiguration = BsonConfiguration(encodeDefaults = false)
106+
)
107+
108+
val registry = CodecRegistries.fromRegistries(
109+
CodecRegistries.fromCodecs(myCustomCodec), collection.codecRegistry
110+
)
111+
// :snippet-end:
112+
113+
val paint = PaintOrder(ObjectId(), "red", 5)
114+
val insertOneResult = collection.withCodecRegistry(registry).insertOne(paint)
115+
assertEquals(paint.id, insertOneResult.insertedId?.asObjectId()?.value)
116+
val result = collection.withDocumentClass<Document>().find().first().toJson()
117+
assertFalse(result.contains("manufacturer"))
118+
collection.drop()
119+
}
120+
121+
}
122+

snooty.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ snappyVersion = "org.xerial.snappy:snappy-java:1.1.8.4"
3434
zstdVersion = "com.github.luben:zstd-jni:1.5.5-2"
3535
logbackVersion = "1.2.11"
3636
log4j2Version = "2.17.1"
37+
serializationVersion = "1.5.1"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@Serializable
2+
data class PaintOrder(
3+
@SerialName("_id") // Use instead of @BsonId
4+
@Contextual val id: ObjectId?,
5+
val color: String,
6+
val qty: Int,
7+
@SerialName("brand")
8+
val manufacturer: String = "Acme" // Use instead of @BsonProperty
9+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
val myCustomCodec = KotlinSerializerCodec.create<PaintOrder>(
2+
bsonConfiguration = BsonConfiguration(encodeDefaults = false)
3+
)
4+
5+
val registry = CodecRegistries.fromRegistries(
6+
CodecRegistries.fromCodecs(myCustomCodec), collection.codecRegistry
7+
)

source/fundamentals/data-formats.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Data Formats
88
- :doc:`/fundamentals/data-formats/document-data-format-bson`
99
- :doc:`/fundamentals/data-formats/document-data-format-extended-json`
1010
- :doc:`/fundamentals/data-formats/documents`
11+
- :doc:`/fundamentals/data-formats/serialization`
1112
- :doc:`/fundamentals/data-formats/codecs`
1213

1314
.. toctree::
@@ -17,4 +18,5 @@ Data Formats
1718
/fundamentals/data-formats/document-data-format-bson
1819
/fundamentals/data-formats/document-data-format-extended-json
1920
/fundamentals/data-formats/documents
21+
/fundamentals/data-formats/serialization
2022
/fundamentals/data-formats/codecs

source/fundamentals/data-formats/codecs.txt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
Codecs
55
======
66

7-
.. default-domain:: mongodb
8-
97
.. contents:: On this page
108
:local:
119
:backlinks: none
@@ -291,6 +289,15 @@ to define the encoding and decoding logic for a custom Kotlin class. We also sho
291289
how you can specify and use your custom implementations to perform insert
292290
and retrieve operations.
293291

292+
.. tip:: Kotlin Serialization
293+
294+
As an alternative to implementing custom codecs, you can use
295+
Kotlin serialization to handle your data encoding and decoding with
296+
``@Serializable`` classes. You might choose Kotlin serialization if you are
297+
already familiar with the framework or prefer to use an idiomatic Kotlin approach.
298+
See the :ref:`Kotlin Serialization <fundamentals-kotlin-serialization>`
299+
documentation for more information.
300+
294301
The following code snippet shows our example custom class called ``Monolight``
295302
and its fields that we want to store and retrieve from a MongoDB collection:
296303

@@ -348,6 +355,3 @@ see the following API Documentation:
348355
- `MongoClientSettings.getDefaultCodecRegistry() <{+api+}/apidocs/mongodb-driver-core/com/mongodb/MongoClientSettings.html#getDefaultCodecRegistry()>`__
349356
- `Codec <{+api+}/apidocs/bson/org/bson/codecs/Codec.html>`__
350357
- `CodecProvider <{+api+}/apidocs/bson/org/bson/codecs/configuration/CodecProvider.html>`__
351-
352-
.. TODO add a section about using kotlinx serialization as a way to
353-
.. bypass needing custom codecs (DOCSP-29226)
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
.. _fundamentals-kotlin-serialization:
2+
3+
====================
4+
Kotlin Serialization
5+
====================
6+
7+
.. contents:: On this page
8+
:local:
9+
:backlinks: none
10+
:depth: 2
11+
:class: singlecol
12+
13+
Overview
14+
--------
15+
16+
The Kotlin driver supports the ``kotlinx.serialization`` library for serializing
17+
and deserializing Kotlin objects.
18+
19+
The driver provides an efficient ``Bson`` serializer that can can be used with
20+
classes marked as ``@Serializable`` to handle the serialization of Kotlin objects
21+
to BSON data.
22+
The ``bson-kotlinx`` library also supports :ref:`custom codecs <kotlin-custom-codec>`
23+
with configurations to encode defaults, encode nulls, and define class
24+
discriminators.
25+
26+
.. note::
27+
28+
To use the ``Codec`` interface instead of the Kotlin serialization library
29+
to specify custom encoding and decoding of Kotlin objects to BSON data,
30+
see the :ref:`Codecs <fundamentals-codecs>` documentation. You might choose
31+
Kotlin serialization if you are already familiar with the framework or
32+
you prefer to use an idiomatic Kotlin approach.
33+
34+
Although you can use the Kotlin driver with the Kotlin serialization ``Json``
35+
library, the ``Json`` serializer does *not* directly support BSON value types such
36+
as ``ObjectId``. You must provide a custom serializer that can handle the
37+
conversion between BSON and JSON.
38+
39+
Supported Types
40+
~~~~~~~~~~~~~~~
41+
42+
The Kotlin driver supports:
43+
44+
- All Kotlin types that are supported by the Kotlin serialization library
45+
- All available :manual:`BSON types </reference/bson-types>`
46+
47+
You can implement a custom serializers to handle any other types. See the
48+
`official Kotlin Documentation <https://kotlinlang.org/docs/serialization.html>`__
49+
for more information.
50+
51+
Add Kotlin Serialization to Your Project
52+
----------------------------------------
53+
54+
The Kotlin driver serialization support depends on the official Kotlin
55+
serialization library. You must add `Kotlin Serialization <https://github.com/Kotlin/kotlinx.serialization>`__
56+
to your project:
57+
58+
.. tabs::
59+
60+
.. tab::
61+
:tabid: Gradle
62+
63+
If you are using `Gradle <https://gradle.org/>`__ to manage your
64+
dependencies, add the following to your ``build.gradle.kts`` dependencies list:
65+
66+
.. code-block:: kotlin
67+
:caption: build.gradle.kts
68+
69+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:{+serializationVersion+}")
70+
71+
.. tab::
72+
:tabid: Maven
73+
74+
If you are using `Maven <https://maven.apache.org/>`__ to manage your
75+
dependencies, add the following to your ``pom.xml`` dependencies list:
76+
77+
.. code-block:: kotlin
78+
:caption: pom.xml
79+
80+
<dependency>
81+
<groupId>org.jetbrains.kotlinx</groupId>
82+
<artifactId>kotlinx-serialization-core</artifactId>
83+
<version>{+serializationVersion+}</version>
84+
</dependency>
85+
86+
Annotate Data Classes
87+
---------------------
88+
89+
To declare a class as serializable, annotate your Kotlin data classes with the
90+
``@Serializable`` annotation from the Kotlin serialization framework.
91+
92+
You can use your data classes in your code as normal after you mark them as serializable.
93+
The Kotlin driver and the Kotlin serialization framework will handle the
94+
BSON serialization and deserialization.
95+
96+
This example shows a simple data class annotated with the following:
97+
98+
- ``@Serializable`` to mark the class as serializable.
99+
- ``@SerialName`` to specify the name of the ``id`` and ``manufacturer`` properties
100+
in the BSON document. This can be used in place of the ``@BsonId`` and
101+
``@BsonProperty`` annotations, which are unsupported in serializable classes.
102+
- ``@Contextual`` to mark the BSON ``id`` property to use the built-in ``ObjectIdSerializer``.
103+
This annotation is required for BSON types to be serialized correctly.
104+
105+
.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.basic-serialization.kt
106+
:language: kotlin
107+
108+
.. note::
109+
110+
You cannot use :ref:`annotations <fundamentals-data-class-annotations>`
111+
from the ``org.bson.codecs.pojo.annotations`` package on ``@Serializable`` data classes.
112+
113+
For more information on serializable classes and available annotation classes,
114+
see the `official Kotlin Serialization <https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md#serializable-classes>`__
115+
documentation.
116+
117+
.. _kotlin-custom-codec:
118+
119+
Customize the Serializer Configuration
120+
--------------------------------------
121+
122+
You can use the ``KotlinSerializerCodec`` class from the ``org.bson.codecs.kotlinx``
123+
package to create a codec for your ``@Serializable`` data classes and
124+
customize what is stored.
125+
126+
Use the ``BsonConfiguration`` class to define the configuration,
127+
including whether to encode defaults, encode nulls, or define class discriminators.
128+
129+
To create a custom codec, install the ``bson-kotlinx`` dependency to your project.
130+
131+
.. tabs::
132+
133+
.. tab::
134+
:tabid: Gradle
135+
136+
If you are using `Gradle <https://gradle.org/>`__ to manage your
137+
dependencies, add the following to your ``build.gradle.kts`` dependencies list:
138+
139+
.. code-block:: kotlin
140+
:caption: build.gradle.kts
141+
142+
implementation("org.mongodb:bson-kotlinx:{+full-version+}")
143+
144+
.. tab::
145+
:tabid: Maven
146+
147+
If you are using `Maven <https://maven.apache.org/>`__ to manage your
148+
dependencies, add the following to your ``pom.xml`` dependencies list:
149+
150+
.. code-block:: kotlin
151+
:caption: pom.xml
152+
153+
<dependency>
154+
<groupId>org.jetbrains.kotlinx</groupId>
155+
<artifactId>bson-kotlinx</artifactId>
156+
<version>{+full-version+}</version>
157+
</dependency>
158+
159+
Then, you can define your codec using the `KotlinSerializerCodec.create() <apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/create.html>`__
160+
method and add it to the registry.
161+
162+
The following example shows how to create a codec using the
163+
``KotlinSerializerCodec.create()`` method and configure it to not encode defaults:
164+
165+
.. code-block:: kotlin
166+
:copyable: true
167+
168+
import org.bson.codecs.configuration.CodecRegistries
169+
import org.bson.codecs.kotlinx.BsonConfiguration
170+
import org.bson.codecs.kotlinx.KotlinSerializerCodec
171+
172+
.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.custom-serialization.kt
173+
:language: kotlin
174+
175+
For more information about the methods and classes mentioned in this section,
176+
see the following API Documentation:
177+
178+
- `KotlinSerializerCodec <{+api-kotlin+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/index.html>`__
179+
- `KotlinSerializerCodec.create() <{+api-kotlin+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/create.html>`__
180+
- `BsonConfiguration <{+api-kotlin+}apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-configuration/index.html>`__
181+

0 commit comments

Comments
 (0)