Docs Menu
Docs Home
/ / /
Kotlin Sync Driver
/

Kotlin Serialization

On this page

  • Overview
  • Supported Types
  • Add the Serialization Dependencies to Your Project
  • Annotate Data Classes
  • Custom Serializer Example
  • Customize the Serializer Configuration
  • Custom Codec Example
  • Polymorphic Serialization
  • Polymorphic Data Classes Example

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.

The Kotlin Sync driver supports the following types:

  • All Kotlin types that are supported by the kotlinx.serialization library

  • All BSON types

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:

build.gradle.kts
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
implementation("org.mongodb:bson-kotlinx:5.1.4")

If you are using Maven to manage your dependencies, add the following to your pom.xml dependencies list:

pom.xml
<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.1.4</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.

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 the id and manufacturer properties in the BSON document. This annotation can be used in place of the @BsonId and @BsonProperty annotations, which are unsupported in serializable classes.

  • @Contextual: Marks the BSON id property to use the built-in ObjectIdSerializer. This annotation is required for the driver to serialize BSON types correctly.

@Serializable
data class PaintOrder(
@SerialName("_id") // Use instead of @BsonId
@Contextual val id: ObjectId?,
val color: String,
val qty: Int,
@SerialName("brand")
val manufacturer: String = "Grumbacher" // 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.

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:

@Serializable
data class PaintOrder(
val color: String,
val qty: Int,
@Serializable(with = InstantAsBsonDateTime::class)
val orderDate: Instant,
)

To learn more about the methods and classes mentioned in this section, see the following API documentation:

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.

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
)

To learn more about the methods and classes mentioned in this section, see the following API documentation:

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.

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:

@Serializable
sealed interface Person {
val name: String
}
@Serializable
data class Student(
@Contextual
@SerialName("_id")
val id: ObjectId,
override val name: String,
val grade: Int,
) : Person
@Serializable
data class Teacher(
@Contextual
@SerialName("_id")
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}}

Back

Specialized Data Formats