Kotlin Serialization
On this page
- Overview
- Supported Types
- Add Kotlin Serialization to Your Project
- Annotate Data Classes
- Custom Serializer Example
- Customize the Serializer Configuration
- Custom Codec Example
- Polymorphic Serialization
- Polymorphic Data Classes Example
- Serialize Dates and Times
- kotlinx-datetime Library
- Example Data Class with Dates and Times
Overview
The Kotlin driver supports the kotlinx.serialization
library for
serializing and deserializing Kotlin objects.
The driver provides an efficient Bson
serializer that you can use with
classes marked as @Serializable
to handle the serialization of Kotlin objects
to BSON data.
You can also install the bson-kotlinx
library to support
custom codecs with configurations to encode
defaults, encode nulls, and define class discriminators.
Note
To learn how to use the Codec
interface instead of the
Kotlin serialization library to specify custom encoding and decoding
of Kotlin objects to BSON data, see the Codecs guide.
You might choose Kotlin serialization if you are already familiar with the framework or if you prefer to use an idiomatic Kotlin approach.
Although you can use the Kotlin driver with the Kotlin serialization Json
library, the Json
serializer does not directly support BSON value types such
as ObjectId
. You must provide a custom serializer that can handle the
conversion between BSON and JSON.
Supported Types
The Kotlin driver supports:
All Kotlin types that are supported by the Kotlin serialization library
All available BSON types
Add Kotlin Serialization to Your Project
Support for serialization in the Kotlin driver depends on the official Kotlin serialization library.
Select from the following tabs to see how to add the serialization dependencies to your project by using the Gradle and Maven package managers:
If you are using Gradle to manage your
dependencies, add the following to your build.gradle.kts
dependencies list:
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1") implementation("org.mongodb:bson-kotlinx:5.2.0")
If you are using Maven to manage your
dependencies, add the following to your pom.xml
dependencies list:
<dependency> <groupId>org.jetbrains.kotlinx</groupId> <artifactId>kotlinx-serialization-core</artifactId> <version>1.5.1</version> </dependency> <dependency> <groupId>org.mongodb</groupId> <artifactId>bson-kotlinx</artifactId> <version>5.2.0</version> </dependency>
Annotate Data Classes
To declare a class as serializable, annotate your Kotlin data classes with the
@Serializable
annotation from the Kotlin serialization framework.
You can use your data classes in your code as normal after you mark them as serializable. The Kotlin driver and the Kotlin serialization framework handle the BSON serialization and deserialization.
This example shows a simple data class annotated with the following:
@Serializable
to mark the class as serializable.@SerialName
to specify the name of theid
andmanufacturer
properties in the BSON document. This can be used in place of the@BsonId
and@BsonProperty
annotations, which are unsupported in serializable classes.@Contextual
to mark the BSONid
property to use the built-inObjectIdSerializer
. This annotation is required for BSON types to be serialized correctly.
data class PaintOrder( // Use instead of @BsonId val id: ObjectId?, val color: String, val qty: Int, val manufacturer: String = "Acme" // Use instead of @BsonProperty )
Note
You cannot use annotations
from the org.bson.codecs.pojo.annotations
package on @Serializable
data classes.
For more information on serializable classes and available annotation classes, see the official Kotlin Serialization documentation.
Custom Serializer Example
You can create a custom serializer to handle how your data is
represented in BSON. The Kotlin driver uses the KSerializer
interface from the kotlinx.serialization
package to implement custom
serializers. You can specify the custom serializer as the parameter to
the @Serializable
annotation for a specific field.
The following example shows how to create a custom
KSerializer
instance to convert a kotlinx.datetime.Instant
to a
BsonDateTime
:
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}") } } }
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:
data class PaintOrder( val color: String, val qty: Int, val orderDate: Instant, )
For more information about the methods and classes mentioned in this section, see the following API documentation:
Customize the Serializer Configuration
You can use the KotlinSerializerCodec
class from the org.bson.codecs.kotlinx
package to create a codec for your @Serializable
data classes and
customize what is stored.
Use the BsonConfiguration
class to define the configuration,
including whether to encode defaults, encode nulls, or define class discriminators.
To create a custom codec, install the bson-kotlinx
dependency to your project. Select from the following tabs to see how to
add the dependency to your project by using the Gradle and
Maven package managers:
If you are using Gradle to manage your
dependencies, add the following to your build.gradle.kts
dependencies list:
implementation("org.mongodb:bson-kotlinx:5.2.0")
If you are using Maven to manage your
dependencies, add the following to your pom.xml
dependencies list:
<dependency> <groupId>org.jetbrains.kotlinx</groupId> <artifactId>bson-kotlinx</artifactId> <version>5.2.0</version> </dependency>
Note
bson-kotlin Dependency
You can also optionally install the bson-kotlin
dependency
through the default codec registry. This dependency uses reflection
and the codec registry to support Kotlin data classes, but it does
not support certain POJO annotations such as BsonDiscriminator
,
BsonExtraElements
, and BsonConstructor
. To learn more, see
the bson-kotlin API documentation.
Generally, we recommend that you install and use the faster
bson-kotlinx
library for codec configuration.
Then, you can define your codec using the KotlinSerializerCodec.create() 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:
import org.bson.codecs.configuration.CodecRegistries import org.bson.codecs.kotlinx.BsonConfiguration import org.bson.codecs.kotlinx.KotlinSerializerCodec
val myCustomCodec = KotlinSerializerCodec.create<PaintOrder>( bsonConfiguration = BsonConfiguration(encodeDefaults = false) ) val registry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(myCustomCodec), collection.codecRegistry )
For more information about the methods and classes mentioned in this section, see the following API documentation:
Polymorphic Serialization
The Kotlin driver natively supports serialization and deserialization
of polymorphic classes. When you mark a sealed interface and data
classes that inherit that interface with the @Serializable
annotation, the driver uses a KSerializer
implementation to handle
conversion of your types to and from BSON.
When you insert an instance of a polymorphic data class into MongoDB,
the driver adds the field _t
, the
discriminator field. The value of this field is the data class name.
Polymorphic Data Classes Example
The following example creates an interface and two data classes that
inherit that interface. In the data classes, the id
field is marked
with the annotations described in the
Annotate Data Classes section:
sealed interface Person { val name: String } data class Student( val id: ObjectId, override val name: String, val grade: Int, ) : Person data class Teacher( val id: ObjectId, override val name: String, val department: String, ) : Person
Then, you can perform operations with data classes as usual. The
following example parametrizes the collection with the Person
interface, then performs operations with the polymorphic classes
Teacher
and Student
. When you retrieve documents, the driver
automatically detects the type based on the discriminator value and
deserializes them accordingly.
val collection = database.getCollection<Person>("school") val teacherDoc = Teacher(ObjectId(), "Vivian Lee", "History") val studentDoc = Student(ObjectId(), "Kate Parker", 10) collection.insertOne(teacherDoc) collection.insertOne(studentDoc) println("Retrieving by using data classes") collection.withDocumentClass<Teacher>() .find(Filters.exists("department")) .first().also { println(it) } collection.withDocumentClass<Student>() .find(Filters.exists("grade")) .first().also { println(it) } println("\nRetrieving by using Person interface") val resultsFlow = collection.withDocumentClass<Person>().find() resultsFlow.collect { println(it) } println("\nRetrieving as Document type") val resultsDocFlow = collection.withDocumentClass<Document>().find() resultsDocFlow.collect { println(it) }
Retrieving by using data classes Teacher(id=..., name=Vivian Lee, department=History) Student(id=..., name=Kate Parker, grade=10) Retrieving by using Person interface Teacher(id=..., name=Vivian Lee, department=History) Student(id=..., name=Kate Parker, grade=10) Retrieving as Document type Document{{_id=..., _t=Teacher, name=Vivian Lee, department=History}} Document{{_id=..., _t=Student, name=Kate Parker, grade=10}}
Serialize Dates and Times
In this section, you can learn about using Kotlin serialization to work with date and time types.
kotlinx-datetime Library
kotlinx-datetime
is a Kotlin library that offers
a high level of control over how your date and time values
are serialized. To use the library, add the kotlinx-datetime
dependency to your project's dependency list.
Select from the following tabs to see how to add the kotlinx-datetime
dependency to your project by using the Gradle and
Maven package managers:
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
<dependency> <groupId>org.jetbrains.kotlinx</groupId> <artifactId>kotlinx-datetime-jvm</artifactId> <version>0.6.1</version> </dependency>
To learn more about this library, see the kotlinx-datetime repository on GitHub.
Example Data Class with Dates and Times
After you add the library dependency, you can implement serializers from
the kotlinx-datetime
library that map your data class field values
to the expected types in BSON.
In this example, the driver serializes the fields of
the Appointment
data class with the following behavior:
name
: The driver serializes the value as a string.date
: The driver uses thekotlinx-datetime
serializer because the field has the@Contextual
annotation.LocalDate
values are serialized as BSON dates.time
: The driver serializes the value as a string because it does not have the@Contextual
annotation. This is the default serialization behavior forLocalTime
values.
data class Appointment( val name: String, val date: LocalDate, val time: LocalTime, )
The following example inserts an instance of the Appointment
data
class into the appointments
collection:
val collection = database.getCollection<Appointment>("appointments") val apptDoc = Appointment( "Daria Smith", LocalDate(2024, 10, 15), LocalTime(hour = 11, minute = 30) ) collection.insertOne(apptDoc)
In MongoDB, the LocalDate
value is stored as a BSON date, and the
time
field is stored as a string by default serialization:
{ "_id": ..., "name": "Daria Smith", "date": { "$date": "2024-10-15T00:00:00.000Z" }, "time": "11:30", }