Docs Menu
Docs Home
/ / /
Kotlin Sync Driver
/

Document Data Format: Data Classes

On this page

  • Overview
  • Serialize and Deserialize a Data Class
  • Example Data Class
  • Insert a Data Class
  • Retrieve a Data Class
  • Specify Component Conversion Using Annotations
  • Annotated Data Class Example
  • Insert an Annotated Data Class
  • Retrieve an Annotated Data Class
  • Operations with Recursive Types

In this guide, you can learn how to store and retrieve data in the MongoDB Kotlin Sync Driver by using Kotlin data classes.

The driver natively supports encoding and decoding Kotlin data classes for MongoDB read and write operations by using the default codec registry. The default codec registry is a collection of classes called codecs that define how to encode and decode Kotlin and Java types.

The code examples in this section reference the following sample data class, which describes a data storage device:

data class DataStorage(val productName: String, val capacity: Double)

You can insert a DataStorage instance as shown in the following code:

val collection = database.getCollection<DataStorage>("data_storage")
val record = DataStorage("tape", 5.0)
collection.insertOne(record)

You can retrieve documents as DataStorage instances and print them as shown in the following code:

val result = collection.find().firstOrNull()
println("${result}")
DataStorage(productName=tape, capacity=5.0)

Tip

Builder Methods and Data Class Properties

You can use the methods from builder classes directly with data class properties by adding the optional Kotlin Sync driver extensions dependency to your application. To learn more and view examples, see the Use Builders with Data Classes guide.

You must specify a class for documents returned from a collection, even if it is different than the class you specified when retrieving the collection.

The following example performs an update to the document represented by the DataStorage data class in the previous example and returns the updated document as a DataStorageAlt type. The operation adds the releaseDate field to the document in which the value of the name field is tape:

// Define a data class for returned documents
data class DataStorageAlt(
val productName: String,
val capacity: Double,
val releaseDate: LocalDate
)
val filter = Filters.eq(DataStorage::productName.name, "tape")
val update = Updates.currentDate("releaseDate")
val options = FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
// Specify the class for returned documents
val newResult = collection
.withDocumentClass<DataStorageAlt>()
.findOneAndUpdate(filter, update, options)
println("Document after update:\n${newResult}")
Document after update:
DataStorageAlt(productName=tape, capacity=5.0, releaseDate=2025-01-24)

This section describes the annotations you can use to configure the serialization behavior of data classes and provides an example to demonstrate the annotation behavior.

You can use the following annotations on data classes:

Annotation Name
Description

BsonId

Marks a property to serialize as the _id property.

BsonProperty

Specifies a custom document field name when converting the data class field to BSON.

BsonRepresentation

Specifies the BSON type MongoDB uses to store the value. Use this annotation only when you must store a value as a different BSON type than the data class property.

WARNING: Your code might throw an exception if you include the BsonRepresentation annotation on a property that you store as the same type as the data class property.

To learn more these property annotations, see to the org.bson.codecs.pojo.annotations Package API documentation.

The code examples in this section reference the following sample data class, which describes a network device:

data class NetworkDevice(
@BsonId
@BsonRepresentation(BsonType.OBJECT_ID)
val deviceId: String,
val name: String,
@BsonProperty("type")
val deviceType: String
)

You can insert a NetworkDevice instance as shown in the following code:

val ntwkColl = database.getCollection<NetworkDevice>("network_devices")
val deviceId = ObjectId().toHexString()
val device = NetworkDevice(deviceId, "Enterprise Wi-fi", "router")
ntwkColl.insertOne(device)

The inserted document in MongoDB resembles the following:

{
"_id": {...},
"name": "Enterprise Wi-fi",
"type": "router"
}

You can retrieve documents as NetworkDevice instances and print them as shown in the following code:

val annotatedClassResult = ntwkColl.find().toList()
println(annotatedClassResult)
[NetworkDevice(deviceId=..., name=Enterprise Wi-fi, deviceType=router)]

The driver natively supports encoding and decoding of recursively defined data classes without causing runtime recursion. This support extends to cycles of multiple data class types in type definitions. The following code provides an example of a recursive data class design:

data class DataClassTree(
val content: String,
val left: DataClassTree?,
val right: DataClassTree?
)

You can perform read and write operations on recursively defined data classes the same way that you do for other data classes. The following code shows how you can execute a find operation on a collection of DataClassTree types:

val treeColl = database.getCollection<DataClassTree>("language_trees")
val treeFilter = Filters.eq("left.left.right.content", "high german")
val treeResult = treeColl.find(treeFilter).firstOrNull()
println(treeResult)
DataClassTree(content=indo-european,
left=DataClassTree(content=germanic, left=DataClassTree(...)),
right=DataClassTree(content=romance, ...))

Back

Specialized Data Formats