Docs Menu
Docs Home
/ / /
Kotlin 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 by 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)

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 driver extensions dependency to your application. To learn more and view examples, see the Use Builders with Data Classes guide.

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=...)

Back

Data Formats