Docs Menu
Docs Home
/ /
Atlas Device SDKs
/ /

Serialization - Kotlin SDK

On this page

  • Realm Data Type Serializers
  • Register a Serializer for a Property
  • Register a Serializer for All Occurrences in a File
  • Automatically Bind Realm Types to Serializers
  • Serialization Output Examples
  • EJSON Encoding for Atlas
  • Add KSerialization to Your Project
  • Stable Encoder
  • Call a Function
  • Custom Function Credentials
  • User Profile and Custom Data
  • Full-Document Encoder
  • Required Imports
  • Define a Serializer
  • Experimental Opt-In
  • Call a Function
  • Custom Function Credentials
  • User Profile and Custom Data
  • Other Serialization Libraries

The Realm Kotlin SDK supports Kotlin Serialization. You can serialize specific Realm data types using stable serializers, or user-defined classes with an experimental full-document serialization API.

The Realm Kotlin SDK provides serializers for the following data types for KSerializer:

Realm Data Type
KSerializer for Type
MutableRealmInt
MutableRealmIntKSerializer::class
RealmAny
RealmAnyKSerializer::class
RealmDictionary
RealmDictionaryKSerializer::class
RealmInstant
RealmInstantKSerializer::class
RealmList
RealmListKSerializer::class
RealmSet
RealmSetKSerializer::class
RealmUUID
RealmUUIDKSerializer::class

The serializers are located in io.realm.kotlin.serializers.

For examples of how Realm serializes the different data types, refer to Serialization Output Examples.

Deserializing Realm data types generates unmanaged data instances.

You can register a serializer for a specific property. Use the @Serializable annotation to bind to a specific Realm data type serializer.

class Frog : RealmObject {
var name: String = ""
@Serializable(RealmListKSerializer::class)
var favoritePonds: RealmList<String> = realmListOf()
}

You can register a serializer for all occurrences of that type within a file by adding the declaration to the top of the file:

@file:UseSerializers(RealmSetKSerializer::class)
import io.realm.kotlin.ext.realmSetOf
import io.realm.kotlin.serializers.RealmSetKSerializer
import io.realm.kotlin.types.RealmSet
import kotlinx.serialization.UseSerializers

Then, any objects that have properties of that type within the file can use the serializer without individually registering it:

// These objects have RealmSet properties that get serializers
// from declaring `@file:UseSerializers(RealmSetKSerializer::class)`.
// No need to individually declare them on every `RealmSet` property in the file.
class Movie : RealmObject {
var movieTitle: String = ""
var actors: RealmSet<String> = realmSetOf()
}
class TVSeries : RealmObject {
var seriesTitle: String = ""
var episodeTitles: RealmSet<String> = realmSetOf()
}

To automatically bind all Realm types to their serializers, you can add a snippet containing all serializers to the top of a file:

@file:UseSerializers(
MutableRealmIntKSerializer::class,
RealmAnyKSerializer::class,
RealmDictionaryKSerializer::class,
RealmInstantKSerializer::class,
RealmListKSerializer::class,
RealmSetKSerializer::class,
RealmUUIDKSerializer::class
)

These examples illustrate how the different Realm data types serialize using a JSON encoder:

Realm Data Type
Serialization Type and Example
MutableRealmInt
Serializes using a regular integer value.

MutableRealmInt.create(35) serializes to 35
RealmAny
Serializes using a map containing a union of all values and its type.

RealmAny.create("hello world") serializes to {"type": "STRING", "string": "hello world"}

RealmAny.create(20) serializes to {"type": "INT", "int": 20}
RealmDictionary
Serializes using a generic list.

realmDictionaryOf("hello" to "world") serializes to {"hello": "world"}
RealmInstant
Serializes as a BsonDateTime.

RealmInstant.now() serializes to {"$date": {"$numberLong": "<millis>"}}
RealmList
Serializes using a generic list.

realmListOf("hello", world) serializes to ["hello", "world"]
RealmSet
Serializes using a generic list.

realmSetOf("hello", world) serializes to ["hello", "world"]
BsonObjectId or ObjectId
Serializes as a BsonObjectId.

ObjectId.create() serializes to {"$oid": <ObjectId bytes as 24-character, big-endian hex string>}
RealmUUID
Serializes as a BsonBinary.

RealmUUID.random() serializes to { "$binary": {"base64": "<payload>", "subType": "<t>"}}
RealmObject

Serializes using the polymorphic setup defined by the user. Do this via the SerializersModule:

val json = Json {
serializersModule = SerializersModule {
polymorphic(RealmObject::class) {
subclass(SerializableSample::class)
}
}
}

New in version 1.9.0.

Realm Kotlin SDK APIs that communicate directly with MongoDB Atlas use EJSON encoding. The SDK offers two types of EJSON encoders:

  • A limited but stable encoder

  • An experimental encoder that offers full document serialization

The APIs that use these encoders include:

The Realm Kotlin SDK's EJSON serialization support depends on the official Kotlin Serialization library. You must add Kotlin Serialization to your project. Use the same version used in your Realm Kotlin SDK version. Refer to the Version Compatibility Matrix in the realm-kotlin GitHub repository for information about the supported dependencies of each version.

The @Serializable annotation in the following examples comes from the Kotlin Serialization framework.

The stable encoder does not support user-defined classes. You can use these argument types with the stable encoder:

  • Primitives

  • BSON

  • MutableRealmInt

  • RealmUUID

  • ObjectId

  • RealmInstant

  • RealmAny

  • Array

  • Collection

  • Map

To return a collection or map, you can use BsonArray or BsonDocument.

You can call a Function using the stable encoder with a valid argument type, and deserialize the result.

In this example, we call the getMailingAddress Function with two string arguments, and get the result as a BsonDocument:

// The `getMailingAddress` function takes a first name and last name and returns an address as a BsonDocument
val address = user.functions.call<BsonDocument>("getMailingAddress", "Bob", "Smith")
assertEquals(address["street"], BsonString("123 Any Street"))

You can create a Credential for use with custom function authentication using the stable encoder as either a map or a BsonDocument:

val credentials = Credentials.customFunction(
mapOf(
"userId" to 500,
"password" to "securePassword"
)
)
val bsonCredentials = Credentials.customFunction(
BsonDocument(
mapOf(
"userId" to BsonInt32(500),
"password" to BsonString("securePassword")
)
)
)
app.login(credentials)

You can access a user profile or custom user data using the stable encoder as a BsonDocument:

val user = app.currentUser!!
val userProfile = user.profileAsBsonDocument()
assertEquals(userProfile["email"], BsonString("my.email@example.com"))
val user = app.currentUser!!
val customUserData = user.customDataAsBsonDocument()
assertEquals(BsonString("blue"), customUserData?.get("favoriteColor"))

The full-document encoder enables you to serialize and deserialize user-defined classes. You can define and use custom KSerializers for your type with Atlas features that communicate directly with MongoDB Atlas using EJSON encoding. The full-document encoder supports contextual serializers.

Important

This is experimental

The current implementation of full document serialization is experimental. Calling these APIs when your project uses a different version of Kotlin Serialization than Realm's dependency causes undefined behavior. Refer to the Version Compatibility Matrix in the realm-kotlin GitHub repository for information about the supported dependencies of each version.

To use this feature, add one or more of the following imports to your file as relevant:

import kotlinx.serialization.Serializable
import io.realm.kotlin.annotations.ExperimentalRealmSerializerApi
import org.mongodb.kbson.ExperimentalKBsonSerializerApi
import kotlinx.serialization.modules.SerializersModule
import io.realm.kotlin.serializers.RealmListKSerializer

When you use serialization in the Realm Kotlin SDK, you can define a serializer in one of two ways:

  • Add the @Serializable annotation to a class

  • Define a custom KSerializer for your type, and pass it to the relevant API

@Serializable
class Person(
val firstName: String,
val lastName: String
)

You can set a custom EJSON serializer for your app in the AppConfiguration, as in a case where you want to use a contextual serializer:

@Serializable
class Frogger(
val name: String,
@Contextual
val date: LocalDateTime
)
AppConfiguration.Builder(FLEXIBLE_APP_ID)
.ejson(
EJson(
serializersModule = SerializersModule {
contextual(DateAsIntsSerializer)
}
)
)
.build()

Because the full-document serialization API is experimental, you must add the relevant @OptIn annotations for the APIs you use.

@OptIn(ExperimentalRealmSerializerApi::class)

You can call a Function using the experimental API with arguments or return types that use custom serializers.

In this example, we call the getMailingAddressForPerson Function with a serialized Person object, and get the result as a deserialized Address object:

@Serializable
class Address(
val street: String,
val city: String,
val state: String,
val country: String,
val postalCode: String
)
@Serializable
class Person(
val firstName: String,
val lastName: String
)
// The `getMailingAddressForPerson` function takes a Person object and returns an Address object using the experimental serializer
val address = user.functions.call<Address>("getMailingAddressForPerson"){
add(Person("Bob", "Smith"))
}
assertEquals(address.street, "123 Any Street")

Tip

Atlas Function calls for the stable serializer and the experimental API serializer share the same method name. When invoking a function with no parameters, you must provide an empty block in the instruction for the experimental API.

val color = user.functions.call<PersonalFavorites>("favouriteColor") {}

You can define a custom serializer for custom function authentication using the experimental API:

@Serializable
class CustomUserCredential(
val userId: Int,
val password: String
)

And use it when creating a custom function credential:

val credentials = Credentials.customFunction(
CustomUserCredential(
userId = 500,
password = "securePassword"
)
)
app.login(credentials)

Define a custom serializer for user profile or custom data:

@Serializable
class UserProfile(
val email: String
)
@Serializable
class UserCustomData(
val favoriteColor: String
)

And use the custom serializer when accessing the user profile or custom user data:

val user = app.currentUser!!
val userProfile = user.profile<UserProfile>()
assertEquals(userProfile.email, "my.email@example.com")
val user = app.currentUser!!
val customUserData = user.customData<UserCustomData>()
assertEquals("blue", customUserData!!.favoriteColor)

Serialization methods used by libraries that depend on reflection, such as GSON do not work with the SDK by default.

This is because the SDK compiler plugin injects a hidden field into object models, prefixed with io_realm_kotlin_. The SDK uses this hidden field to manage internal object state. Any library that relies on fields instead of getters and setters needs to ignore this hidden field.

To use the SDK with external libraries such as GSON, exclude the hidden fields from serialization using a prefix match:

var gson: Gson = GsonBuilder()
.setExclusionStrategies(object: ExclusionStrategy {
override fun shouldSkipField(f: FieldAttributes?): Boolean =
f?.name?.startsWith("io_realm_kotlin_") ?: false
override fun shouldSkipClass(clazz: Class<*>?): Boolean =
false
})
.create()
← React to Changes