Skip to content

Commit 48c5c3d

Browse files
committed
DOCSP-35884: custom serializer example (#148)
* DOCSP-35884: custom serializer example * rearrange structure * add intro to dependency tabs * small fix (cherry picked from commit 5015f98)
1 parent bc34268 commit 48c5c3d

File tree

7 files changed

+137
-15
lines changed

7 files changed

+137
-15
lines changed

examples/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
3030
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
3131
implementation("org.mongodb:bson-kotlinx:4.10.0-alpha1")
32+
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
3233
}
3334

3435
tasks.test {

examples/src/test/kotlin/KotlinXSerializationTest.kt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
import com.mongodb.client.model.Filters.eq
23
import com.mongodb.kotlin.client.coroutine.MongoClient
34
import config.getConfig
45
import kotlinx.coroutines.flow.first
@@ -8,11 +9,22 @@ import kotlinx.serialization.Contextual
89
import kotlinx.serialization.ExperimentalSerializationApi
910
import kotlinx.serialization.SerialName
1011
import kotlinx.serialization.Serializable
12+
import kotlinx.datetime.*
13+
import kotlinx.serialization.KSerializer
14+
import kotlinx.serialization.SerializationException
15+
import kotlinx.serialization.descriptors.PrimitiveKind
16+
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
17+
import kotlinx.serialization.descriptors.SerialDescriptor
18+
import kotlinx.serialization.encoding.Decoder
19+
import kotlinx.serialization.encoding.Encoder
1120
import kotlinx.serialization.json.Json
1221
import kotlinx.serialization.json.encodeToJsonElement
22+
import org.bson.BsonDateTime
1323
import org.bson.Document
1424
import org.bson.codecs.configuration.CodecRegistries
1525
import org.bson.codecs.kotlinx.BsonConfiguration
26+
import org.bson.codecs.kotlinx.BsonDecoder
27+
import org.bson.codecs.kotlinx.BsonEncoder
1628
import org.bson.codecs.kotlinx.KotlinSerializerCodec
1729
import org.bson.codecs.kotlinx.ObjectIdSerializer
1830
import org.bson.types.ObjectId
@@ -22,6 +34,7 @@ import org.junit.jupiter.api.Assertions.assertFalse
2234

2335
import org.junit.jupiter.api.Test
2436
import org.junit.jupiter.api.TestInstance
37+
import java.util.Date
2538

2639
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
2740
internal class KotlinXSerializationTest {
@@ -118,5 +131,52 @@ internal class KotlinXSerializationTest {
118131
collection.drop()
119132
}
120133

134+
// :snippet-start: kserializer
135+
object InstantAsBsonDateTime : KSerializer<Instant> {
136+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantAsBsonDateTime", PrimitiveKind.LONG)
137+
138+
override fun serialize(encoder: Encoder, value: Instant) {
139+
when (encoder) {
140+
is BsonEncoder -> encoder.encodeBsonValue(BsonDateTime(value.toEpochMilliseconds()))
141+
else -> throw SerializationException("Instant is not supported by ${encoder::class}")
142+
}
143+
}
144+
145+
override fun deserialize(decoder: Decoder): Instant {
146+
return when (decoder) {
147+
is BsonDecoder -> Instant.fromEpochMilliseconds(decoder.decodeBsonValue().asDateTime().value)
148+
else -> throw SerializationException("Instant is not supported by ${decoder::class}")
149+
}
150+
}
151+
}
152+
// :snippet-end:
153+
154+
@Test
155+
fun customKSerializerTest() = runBlocking {
156+
// :snippet-start: kserializer-dataclass
157+
@Serializable
158+
data class PaintOrder(
159+
val color: String,
160+
val qty: Int,
161+
@Serializable(with = InstantAsBsonDateTime::class)
162+
val orderDate: Instant,
163+
)
164+
// :snippet-end:
165+
166+
val collection = database.getCollection<PaintOrder>("orders")
167+
val paintOrder = PaintOrder("magenta", 5, Instant.parse("2024-01-15T00:00:00Z"))
168+
val insertOneResult = collection.insertOne(paintOrder)
169+
170+
val resultsFlow = collection.withDocumentClass<Document>()
171+
.find(eq(PaintOrder::color.name, "magenta"))
172+
.firstOrNull()
173+
174+
if (resultsFlow != null) {
175+
assertEquals(resultsFlow["orderDate"], Date.from(java.time.Instant.parse("2024-01-15T00:00:00Z")))
176+
}
177+
178+
collection.drop()
179+
}
180+
121181
}
122182

snooty.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ driver-long = "MongoDB Kotlin Driver"
2222
version = "4.10"
2323
full-version = "{+version+}.1"
2424
mdb-server = "MongoDB server"
25-
kotlin-docs = "https://kotlinlang.org/docs"
25+
kotlin-docs = "https://kotlinlang.org"
2626

2727
package-name-org = "mongodb-org"
2828
api = "https://mongodb.github.io/mongo-java-driver/{+version+}"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@Serializable
2+
data class PaintOrder(
3+
val color: String,
4+
val qty: Int,
5+
@Serializable(with = InstantAsBsonDateTime::class)
6+
val orderDate: Instant,
7+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
object InstantAsBsonDateTime : KSerializer<Instant> {
2+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantAsBsonDateTime", PrimitiveKind.LONG)
3+
4+
override fun serialize(encoder: Encoder, value: Instant) {
5+
when (encoder) {
6+
is BsonEncoder -> encoder.encodeBsonValue(BsonDateTime(value.toEpochMilliseconds()))
7+
else -> throw SerializationException("Instant is not supported by ${encoder::class}")
8+
}
9+
}
10+
11+
override fun deserialize(decoder: Decoder): Instant {
12+
return when (decoder) {
13+
is BsonDecoder -> Instant.fromEpochMilliseconds(decoder.decodeBsonValue().asDateTime().value)
14+
else -> throw SerializationException("Instant is not supported by ${decoder::class}")
15+
}
16+
}
17+
}

source/fundamentals/data-formats/serialization.txt

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Kotlin Serialization
2020
Overview
2121
--------
2222

23-
The Kotlin driver supports the ``kotlinx.serialization`` library for
23+
The {+driver-short+} supports the ``kotlinx.serialization`` library for
2424
serializing and deserializing Kotlin objects.
2525

2626
The driver provides an efficient ``Bson`` serializer that you can use with
@@ -41,29 +41,28 @@ defaults, encode nulls, and define class discriminators.
4141
You might choose Kotlin serialization if you are already familiar
4242
with the framework or if you prefer to use an idiomatic Kotlin approach.
4343

44-
Although you can use the Kotlin driver with the Kotlin serialization ``Json``
44+
Although you can use the {+driver-short+} with the Kotlin serialization ``Json``
4545
library, the ``Json`` serializer does *not* directly support BSON value types such
4646
as ``ObjectId``. You must provide a custom serializer that can handle the
4747
conversion between BSON and JSON.
4848

4949
Supported Types
5050
~~~~~~~~~~~~~~~
5151

52-
The Kotlin driver supports:
52+
The {+driver-short+} supports:
5353

5454
- All Kotlin types that are supported by the Kotlin serialization library
5555
- All available :manual:`BSON types </reference/bson-types>`
5656

57-
You can implement a custom serializers to handle any other types. See the
58-
`official Kotlin Documentation <https://kotlinlang.org/docs/serialization.html>`__
59-
for more information.
60-
6157
Add Kotlin Serialization to Your Project
6258
----------------------------------------
6359

64-
The Kotlin driver serialization support depends on the official Kotlin
65-
serialization library. You must add `Kotlin Serialization <https://github.com/Kotlin/kotlinx.serialization>`__
66-
to your project:
60+
Support for serialization in the {+driver-short+} depends on the official `Kotlin
61+
serialization library <https://github.com/Kotlin/kotlinx.serialization>`__.
62+
63+
Select from the following tabs to see how to add the serialization
64+
dependencies to your project by using the :guilabel:`Gradle` and
65+
:guilabel:`Maven` package managers:
6766

6867
.. tabs::
6968

@@ -106,7 +105,7 @@ To declare a class as serializable, annotate your Kotlin data classes with the
106105
``@Serializable`` annotation from the Kotlin serialization framework.
107106

108107
You can use your data classes in your code as normal after you mark them as serializable.
109-
The Kotlin driver and the Kotlin serialization framework will handle the
108+
The {+driver-short+} and the Kotlin serialization framework handle the
110109
BSON serialization and deserialization.
111110

112111
This example shows a simple data class annotated with the following:
@@ -130,6 +129,38 @@ For more information on serializable classes and available annotation classes,
130129
see the `official Kotlin Serialization <https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md#serializable-classes>`__
131130
documentation.
132131

132+
Custom Serializer Example
133+
~~~~~~~~~~~~~~~~~~~~~~~~~
134+
135+
You can create a custom serializer to handle how your data is
136+
represented in BSON. The {+driver-short+} uses the ``KSerializer``
137+
interface from the ``kotlinx.serialization`` package to implement custom
138+
serializers. You can specify the custom serializer as the parameter to
139+
the ``@Serializable`` annotation for a specific field.
140+
141+
The following example shows how to create a custom
142+
``KSerializer`` instance to convert a ``kotlinx.datetime.Instant`` to a
143+
``BsonDateTime``:
144+
145+
.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.kserializer.kt
146+
:language: kotlin
147+
148+
The following code shows the ``PaintOrder`` data class in which the
149+
``orderDate`` field has an annotation that specifies the custom
150+
serializer class defined in the preceding code:
151+
152+
.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.kserializer-dataclass.kt
153+
:language: kotlin
154+
:emphasize-lines: 5
155+
156+
For more information about the methods and classes mentioned in this section,
157+
see the following API documentation:
158+
159+
- `KSerializer <{+kotlin-docs+}/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/>`__
160+
- `Instant <{+kotlin-docs+}/api/kotlinx-datetime/kotlinx-datetime/kotlinx.datetime/-instant/>`__
161+
- `BsonEncoder <{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-encoder/index.html>`__
162+
- `BsonDecoder <{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-decoder/index.html>`__
163+
133164
.. _kotlin-custom-codec:
134165

135166
Customize the Serializer Configuration
@@ -142,7 +173,10 @@ customize what is stored.
142173
Use the ``BsonConfiguration`` class to define the configuration,
143174
including whether to encode defaults, encode nulls, or define class discriminators.
144175

145-
To create a custom codec, install the ``bson-kotlinx`` dependency to your project.
176+
To create a custom codec, install the ``bson-kotlinx``
177+
dependency to your project. Select from the following tabs to see how to
178+
add the dependency to your project by using the :guilabel:`Gradle` and
179+
:guilabel:`Maven` package managers:
146180

147181
.. tabs::
148182

@@ -189,6 +223,9 @@ Then, you can define your codec using the
189223
<{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/index.html>`__
190224
method and add it to the registry.
191225

226+
Custom Codec Example
227+
~~~~~~~~~~~~~~~~~~~~
228+
192229
The following example shows how to create a codec using the
193230
``KotlinSerializerCodec.create()`` method and configure it to not encode defaults:
194231

@@ -203,7 +240,7 @@ The following example shows how to create a codec using the
203240
:language: kotlin
204241

205242
For more information about the methods and classes mentioned in this section,
206-
see the following API Documentation:
243+
see the following API documentation:
207244

208245
- `KotlinSerializerCodec <{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/index.html>`__
209246
- `KotlinSerializerCodec.create() <{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/create.html>`__

source/quick-start.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Install Kotlin
4848

4949
Make sure that your system has Kotlin installed and running on JDK 1.8 or later.
5050
For more information on getting started with Kotlin/JVM development,
51-
refer to `Get started with Kotlin/JVM <{+kotlin-docs+}/jvm-get-started.html>`__
51+
refer to `Get started with Kotlin/JVM <{+kotlin-docs+}/docs/jvm-get-started.html>`__
5252
in the Kotlin language documentation.
5353

5454
Create the Project

0 commit comments

Comments
 (0)