Docs Menu

Docs HomeDevelop ApplicationsMongoDB DriversKotlin Coroutine

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
  • Example Annotated Data Class
  • 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 Driver using Kotlin data classes.

The driver natively supports encoding and decoding Kotlin data classes for MongoDB read and write operations 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 collection = database.getCollection<DataStorage>("data_storage_devices")
// Retrieve and print the documents as data classes
val resultsFlow = collection.find()
resultsFlow.collect { println(it) }
DataStorage(productName=tape, capacity=5.0)

You 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 NewDataStorage type. The operation adds the releaseDate field to the document with a name value of tape:

// Define a data class for returned documents
data class NewDataStorage(
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 as the type parameter in withDocumentClass()
val result = collection
.withDocumentClass<NewDataStorage>()
.findOneAndUpdate(filter, update, options)
println("Updated document: ${result}")
Updated document: NewDataStorage(productName=tape, capacity=5.0, releaseDate=2023-06-15)

For more information about this feature, see Specify Return Type in the Databases and Collections guide.

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 need to 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.

For reference information on these property annotations, refer to the org.bson.codecs.pojo.annotations package.

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 collection = database.getCollection<NetworkDevice>("network_devices")
// Insert the record
val deviceId = ObjectId().toHexString()
val device = NetworkDevice(deviceId, "Enterprise Wi-fi", "router")
collection.insertOne(device)

The inserted document in MongoDB should resemble the following:

{
_id: ObjectId("fedc..."),
name: 'Enterprise Wi-fi',
type: 'router'
}

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

val collection = database.getCollection<NetworkDevice>("network_devices")
// Return all documents in the collection as data classes
val resultsFlow = collection.find()
resultsFlow.collect { println(it) }
NetworkDevice(deviceId=645cf..., 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 you would for other data classes. The following code shows how you can execute a find operation on a collection of DataClassTree types:

val collection = database.getCollection<DataClassTree>("myCollection")
val filter = Filters.eq("left.left.right.content", "high german")
val resultsFlow = collection.find(filter)
resultsFlow.collect { println(it) }
DataClassTree(content=indo-european, left=DataClassTree(content=germanic, left=DataClassTree(content=german, left=null, right=DataClassTree(content=high german, ...)), right=...)
←  Data FormatsDocument Data Format: BSON →