Overview
You can use the kotlinx.serialization library when
serializing and deserializing Kotlin objects in your application.
The driver provides an efficient Bson serializer that you can use with
classes marked with the @Serializable annotation to handle the
serialization of Kotlin objects to BSON data.
Although you can use 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.
We recommend installing the bson-kotlinx library to support
custom codecs that have configurations to encode defaults, nulls, and
define class discriminators.
Note
To learn how to use the Codec interface instead of the
kotlinx.serialization library to specify custom
serialization behavior, see the Codecs guide.
You might choose to use Kotlin serialization if you are already familiar with the library or if you prefer to use an idiomatic approach.
Supported Types
The Kotlin Sync driver supports the following types:
All Kotlin types that are supported by the
kotlinx.serializationlibraryAll BSON types
Add the Serialization Dependencies to Your Project
You must install the official Kotlin serialization library,
kotlinx.serialization, to serialize and deserialize data in your
application. To learn more about this library, see the
kotlinx.serialization GitHub repository.
Select from the following tabs to see how to add the serialization dependencies to your project by using either Gradle or Maven:
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.6.0") implementation("org.mongodb:bson-kotlinx:5.4.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.6.0</version> </dependency> <dependency> <groupId>org.mongodb</groupId> <artifactId>bson-kotlinx</artifactId> <version>5.4.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.
Annotate Data Classes
To declare a class as serializable, annotate your Kotlin data
classes with the @Serializable annotation.
You can use your data classes in your code as you use any other data class after you mark them as serializable. The Kotlin Sync driver and the Kotlin serialization framework handle BSON serialization and deserialization.
This example shows a sample data class with the following annotations:
@Serializable: Marks the class as serializable.@SerialName: Specifies the name of theidandmanufacturerproperties in the BSON document. This annotation can be used in place of the@BsonIdand@BsonPropertyannotations, which are unsupported in serializable classes.@Contextual: Marks the BSONidproperty to use the built-inObjectIdSerializer. This annotation is required for the driver to serialize BSON types correctly.
data class PaintOrder( // Use instead of @BsonId val id: ObjectId?, val color: String, val qty: Int, val manufacturer: String? = null // Use instead of @BsonProperty )
Note
POJO Annotations Limitation
You cannot use annotations from the
org.bson.codecs.pojo.annotations package on a data class marked
with the @Serializable annotation.
To learn more about serializable classes and available annotations,
see Serializable classes
in the kotlinx.serialization library documentation.
Query Annotated Classes
You can query annotated data classes by using the Filters class
from the mongodb-driver-kotlin-extensions package. This class allows
you to create query filters on field names annotated with the
@SerialName annotation.
Tip
To use the Filters class from the extension library, you must add the
mongodb-driver-kotlin-extensions dependency to your project. For more
information, see the Add Kotlin Extensions to Your Project section of the
Use Builders with Data Classes guide.
The following example queries the orders collection for documents that
have a brand field value of "Sherwin-Williams":
val queryFilter = eq(PaintOrder::manufacturer, "Sherwin-Williams") val results = collection.find(queryFilter).toList()
The preceding code creates a query filter that references the
PaintOrder::manufacturer property but queries the brand
BSON field. The driver automatically converts the data class property name
to the field name specified in the @SerialName annotation.
Custom Serializer Example
You can create a custom serializer to handle how your data is
represented in BSON. The Kotlin Sync driver uses the KSerializer
interface from the kotlinx.serialization library 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.toEpochMilli())) else -> throw SerializationException("Instant is not supported by ${encoder::class}") } } override fun deserialize(decoder: Decoder): Instant { return when (decoder) { is BsonDecoder -> Instant.ofEpochMilli(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, )
To learn more about the methods and classes mentioned in this section, see the following API documentation:
KSerializer in the Kotlin documentation
Instant in the Kotlin 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 the driver stores in MongoDB.
Use the BsonConfiguration class to define the configuration,
which can include whether to encode defaults, encode nulls, or define
class discriminators.
To create a custom codec, your project must have the bson-kotlinx
dependency. See the Add the Serialization Dependencies to Your Project section of this
guide for installation instructions.
You can define your codec by using the KotlinSerializerCodec.create() method, then you can add the codec to the registry.
Custom Codec Example
The following example shows how to create a codec by using the
KotlinSerializerCodec.create() method and then configure it to not
encode defaults:
val myCustomCodec = KotlinSerializerCodec.create<PaintOrder>( bsonConfiguration = BsonConfiguration(encodeDefaults = false) ) val registry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(myCustomCodec), collection.codecRegistry )
Implement Snake Case Naming Strategy
When using bson-kotlinx package v5.4 or later, you can direct the driver to
serialize data class fields names written in camel case to snake case in MongoDB.
The following example shows how to create and register a custom codec
to convert data class field names into snake case by setting the
bsonNamingStrategy parameter in a codec:
val myCustomCodec = KotlinSerializerCodec.create<PaintOrder>( bsonConfiguration = BsonConfiguration(bsonNamingStrategy = BsonNamingStrategy.SNAKE_CASE) ) val registry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(myCustomCodec), collection.codecRegistry )
To learn more about the methods and classes mentioned in this section, see the following API documentation:
Polymorphic Serialization
The Kotlin Sync 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") val resultTeacher = collection.withDocumentClass<Teacher>() .find(Filters.exists("department")) .firstOrNull() println(resultTeacher) val resultStudent = collection.withDocumentClass<Student>() .find(Filters.exists("grade")) .firstOrNull() println(resultStudent) println("\nRetrieving by using Person interface") val resultsPerson = collection.withDocumentClass<Person>().find() resultsPerson.forEach { result -> println(result) } println("\nRetrieving as Document type") val resultsDocument = collection.withDocumentClass<Document>().find() resultsDocument.forEach { result -> println(result) }
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-datetimeserializer because the field has the@Contextualannotation.LocalDatevalues are serialized as BSON dates.time: The driver serializes the value as a string because it does not have the@Contextualannotation. This is the default serialization behavior forLocalTimevalues.
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", }