Skip to content

Commit

Permalink
DOCSP-35884: custom serializer example
Browse files Browse the repository at this point in the history
  • Loading branch information
rustagir committed Jan 30, 2024
1 parent a5cbf35 commit 7496cee
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
implementation("org.mongodb:bson-kotlinx:4.10.0-alpha1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
}

tasks.test {
Expand Down
60 changes: 60 additions & 0 deletions examples/src/test/kotlin/KotlinXSerializationTest.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

import com.mongodb.client.model.Filters.eq
import com.mongodb.kotlin.client.coroutine.MongoClient
import config.getConfig
import kotlinx.coroutines.flow.first
Expand All @@ -8,11 +9,22 @@ import kotlinx.serialization.Contextual
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.datetime.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement
import org.bson.BsonDateTime
import org.bson.Document
import org.bson.codecs.configuration.CodecRegistries
import org.bson.codecs.kotlinx.BsonConfiguration
import org.bson.codecs.kotlinx.BsonDecoder
import org.bson.codecs.kotlinx.BsonEncoder
import org.bson.codecs.kotlinx.KotlinSerializerCodec
import org.bson.codecs.kotlinx.ObjectIdSerializer
import org.bson.types.ObjectId
Expand All @@ -22,6 +34,7 @@ import org.junit.jupiter.api.Assertions.assertFalse

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.util.Date

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class KotlinXSerializationTest {
Expand Down Expand Up @@ -118,5 +131,52 @@ internal class KotlinXSerializationTest {
collection.drop()
}

// :snippet-start: kserializer
object InstantAsBsonDateTime : KSerializer<Instant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantAsBsonDateTime", PrimitiveKind.LONG)

override fun serialize(encoder: Encoder, value: Instant) {
when (encoder) {
is BsonEncoder -> encoder.encodeBsonValue(BsonDateTime(value.toEpochMilliseconds()))
else -> throw SerializationException("Instant is not supported by ${encoder::class}")
}
}

override fun deserialize(decoder: Decoder): Instant {
return when (decoder) {
is BsonDecoder -> Instant.fromEpochMilliseconds(decoder.decodeBsonValue().asDateTime().value)
else -> throw SerializationException("Instant is not supported by ${decoder::class}")
}
}
}
// :snippet-end:

@Test
fun customKSerializerTest() = runBlocking {
// :snippet-start: kserializer-dataclass
@Serializable
data class PaintOrder(
val color: String,
val qty: Int,
@Serializable(with = InstantAsBsonDateTime::class)
val orderDate: Instant,
)
// :snippet-end:

val collection = database.getCollection<PaintOrder>("orders")
val paintOrder = PaintOrder("magenta", 5, Instant.parse("2024-01-15T00:00:00Z"))
val insertOneResult = collection.insertOne(paintOrder)

val resultsFlow = collection.withDocumentClass<Document>()
.find(eq(PaintOrder::color.name, "magenta"))
.firstOrNull()

if (resultsFlow != null) {
assertEquals(resultsFlow["orderDate"], Date.from(java.time.Instant.parse("2024-01-15T00:00:00Z")))
}

collection.drop()
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@Serializable
data class PaintOrder(
val color: String,
val qty: Int,
@Serializable(with = InstantAsBsonDateTime::class)
val orderDate: Instant,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
object InstantAsBsonDateTime : KSerializer<Instant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantAsBsonDateTime", PrimitiveKind.LONG)

override fun serialize(encoder: Encoder, value: Instant) {
when (encoder) {
is BsonEncoder -> encoder.encodeBsonValue(BsonDateTime(value.toEpochMilliseconds()))
else -> throw SerializationException("Instant is not supported by ${encoder::class}")
}
}

override fun deserialize(decoder: Decoder): Instant {
return when (decoder) {
is BsonDecoder -> Instant.fromEpochMilliseconds(decoder.decodeBsonValue().asDateTime().value)
else -> throw SerializationException("Instant is not supported by ${decoder::class}")
}
}
}
22 changes: 22 additions & 0 deletions source/fundamentals/data-formats/serialization.txt
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ Then, you can define your codec using the
<{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/index.html>`__
method and add it to the registry.

Custom Codec Example
~~~~~~~~~~~~~~~~~~~~

The following example shows how to create a codec using the
``KotlinSerializerCodec.create()`` method and configure it to not encode defaults:

Expand All @@ -202,6 +205,25 @@ The following example shows how to create a codec using the
.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.custom-serialization.kt
:language: kotlin

Custom Serializer Example
~~~~~~~~~~~~~~~~~~~~~~~~~

You can create a custom serializer to handle the conversion of different
types to BSON. The following example shows how to create a custom
``KSerializer`` instance to convert a ``kotlinx.datetime.Instant`` to a
``BsonDateTime``:

.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.kserializer.kt
:language: kotlin

The following code shows the ``PaintOrder`` data class in which the
``orderDate`` field has an annotation that specifies the custom
serializer class defined in the preceding code:

.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.kserializer-dataclass.kt
:language: kotlin
:emphasize-lines: 5

For more information about the methods and classes mentioned in this section,
see the following API Documentation:

Expand Down

0 comments on commit 7496cee

Please sign in to comment.