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.
Realm Data Type Serializers
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.
Register a Serializer for a Property
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 = "" var favoritePonds: RealmList<String> = realmListOf() }
Register a Serializer for All Occurrences in a File
You can register a serializer for all occurrences of that type within a file by adding the declaration to the top of the file:
class) (RealmSetKSerializer::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() }
Automatically Bind Realm Types to Serializers
To automatically bind all Realm types to their serializers, you can add a snippet containing all serializers to the top of a file:
( MutableRealmIntKSerializer::class, RealmAnyKSerializer::class, RealmDictionaryKSerializer::class, RealmInstantKSerializer::class, RealmListKSerializer::class, RealmSetKSerializer::class, RealmUUIDKSerializer::class )
Serialization Output Examples
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:
|
EJSON Encoding for Atlas
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:
App Services Function calls
Credentials with custom function authentication
User profile and custom user data
Add KSerialization to Your Project
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.
Stable Encoder
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
.
Call a Function
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"))
Custom Function Credentials
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)
User Profile and Custom Data
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"))
Full-Document Encoder
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.
Required Imports
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
Define a Serializer
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 classDefine a custom KSerializer for your type, and pass it to the relevant API
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:
class Frogger( val name: String, val date: LocalDateTime ) AppConfiguration.Builder(FLEXIBLE_APP_ID) .ejson( EJson( serializersModule = SerializersModule { contextual(DateAsIntsSerializer) } ) ) .build()
Experimental Opt-In
Because the full-document serialization API is experimental, you must add
the relevant @OptIn
annotations for the APIs you use.
Call a Function
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:
class Address( val street: String, val city: String, val state: String, val country: String, val postalCode: String ) 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") {}
Custom Function Credentials
You can define a custom serializer for custom function authentication using the experimental API:
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)
User Profile and Custom Data
Define a custom serializer for user profile or custom data:
class UserProfile( val email: String )
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)
Other Serialization Libraries
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()