All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.realm.kotlin.serializers.RealmKSerializers.kt Maven / Gradle / Ivy

Go to download

Library code for Realm Kotlin. This artifact is not supposed to be consumed directly, but through 'io.realm.kotlin:gradle-plugin:1.11.1' instead.

There is a newer version: 3.0.0
Show newest version
/*
 * Copyright 2023 Realm Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.realm.kotlin.serializers

import io.realm.kotlin.ext.asRealmObject
import io.realm.kotlin.ext.toRealmDictionary
import io.realm.kotlin.ext.toRealmList
import io.realm.kotlin.ext.toRealmSet
import io.realm.kotlin.internal.asBsonDateTime
import io.realm.kotlin.internal.asRealmInstant
import io.realm.kotlin.types.MutableRealmInt
import io.realm.kotlin.types.RealmAny
import io.realm.kotlin.types.RealmAny.Type
import io.realm.kotlin.types.RealmDictionary
import io.realm.kotlin.types.RealmInstant
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.RealmSet
import io.realm.kotlin.types.RealmUUID
import kotlinx.serialization.Contextual
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.SetSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.modules.SerializersModule
import org.mongodb.kbson.BsonBinary
import org.mongodb.kbson.BsonBinarySubType
import org.mongodb.kbson.BsonDateTime
import org.mongodb.kbson.BsonObjectId
import org.mongodb.kbson.Decimal128

/**
 * KSerializer implementation for [RealmList]. Serialization is done as a generic list structure,
 * whilst deserialization is done into an unmanaged [RealmList].
 *
 * It supports any serializable type as a type argument.
 *
 * The serializer must be registered per property:
 * ```
 * class Example : RealmObject {
 *     @Serializable(RealmListKSerializer::class)
 *     var myList: RealmList = realmListOf()
 * }
 * ```
 * or per file:
 * ```
 * @file:UseSerializers(RealmListKSerializer::class)
 *
 * class Example : RealmObject {
 *     var myList: RealmList = realmListOf()
 * }
 * ```
 *
 * Adding the following code snippet to a Kotlin file would conveniently register any field using a
 * Realm datatype to its correspondent serializer:
 *
 * ```
 * @file:UseSerializers(
 *     RealmListKSerializer::class,
 *     RealmSetKSerializer::class,
 *     RealmAnyKSerializer::class,
 *     RealmInstantKSerializer::class,
 *     MutableRealmIntKSerializer::class,
 *     RealmUUIDKSerializer::class
 * )
 * ```
 *
 * Serializers for all Realm data types can be found in [io.realm.kotlin.serializers].
 */
public class RealmListKSerializer(elementSerializer: KSerializer) :
    KSerializer> {
    private val serializer = ListSerializer(elementSerializer)

    override val descriptor: SerialDescriptor =
        serializer.descriptor

    override fun deserialize(decoder: Decoder): RealmList =
        serializer.deserialize(decoder).toRealmList()

    override fun serialize(encoder: Encoder, value: RealmList) {
        serializer.serialize(encoder, value)
    }
}

/**
 * KSerializer implementation for [RealmSet]. Serialization is done as a generic list structure,
 * whilst deserialization is done into an unmanaged [RealmSet].
 *
 * It supports any serializable type as a type argument.
 *
 * The serializer must be registered per property:
 * ```
 * class Example : RealmObject {
 *     @Serializable(RealmSetKSerializer::class)
 *     var mySet: RealmSet = realmSetOf()
 * }
 * ```
 * or per file:
 * ```
 * @file:UseSerializers(RealmSetKSerializer::class)
 *
 * class Example : RealmObject {
 *     var mySet: RealmSet = realmSetOf()
 * }
 * ```
 *
 * Adding the following code snippet to a Kotlin file would conveniently register any field using a
 * Realm datatype to its correspondent serializer:
 *
 * ```
 * @file:UseSerializers(
 *     RealmListKSerializer::class,
 *     RealmSetKSerializer::class,
 *     RealmAnyKSerializer::class,
 *     RealmInstantKSerializer::class,
 *     MutableRealmIntKSerializer::class,
 *     RealmUUIDKSerializer::class
 * )
 * ```
 *
 * Serializers for all Realm data types can be found in [io.realm.kotlin.serializers].
 */
public class RealmSetKSerializer(elementSerializer: KSerializer) : KSerializer> {
    private val serializer = SetSerializer(elementSerializer)

    override val descriptor: SerialDescriptor =
        serializer.descriptor

    override fun deserialize(decoder: Decoder): RealmSet =
        serializer.deserialize(decoder).toRealmSet()

    override fun serialize(encoder: Encoder, value: RealmSet) {
        serializer.serialize(encoder, value)
    }
}

/**
 * KSerializer implementation for [RealmDictionary]. Serialization is done as a generic map structure,
 * whilst deserialization is done into an unmanaged [RealmDictionary].
 *
 * It supports any serializable type as a type argument.
 *
 * The serializer must be registered per property:
 * ```
 * class Example : RealmObject {
 *     @Serializable(RealmDictionaryKSerializer::class)
 *     var myDictionary: RealmDictionary = realmDictionaryOf()
 * }
 * ```
 * or per file:
 * ```
 * @file:UseSerializers(RealmDictionaryKSerializer::class)
 *
 * class Example : RealmObject {
 *     var myDictionary: RealmDictionary = realmDictionaryOf()
 * }
 * ```
 *
 * Adding the following code snippet to a Kotlin file would conveniently register any field using a
 * Realm datatype to its correspondent serializer:
 *
 * ```
 * @file:UseSerializers(
 *     RealmListKSerializer::class,
 *     RealmSetKSerializer::class,
 *     RealmAnyKSerializer::class,
 *     RealmInstantKSerializer::class,
 *     MutableRealmIntKSerializer::class,
 *     RealmUUIDKSerializer::class
 * )
 * ```
 *
 * Serializers for all Realm data types can be found in [io.realm.kotlin.serializers].
 */
public class RealmDictionaryKSerializer(elementSerializer: KSerializer) :
    KSerializer> {
    private val serializer = MapSerializer(String.serializer(), elementSerializer)

    override val descriptor: SerialDescriptor =
        serializer.descriptor

    override fun deserialize(decoder: Decoder): RealmDictionary =
        serializer.deserialize(decoder).toRealmDictionary()

    override fun serialize(encoder: Encoder, value: RealmDictionary) {
        serializer.serialize(encoder, value)
    }
}

/**
 * KSerializer implementation for [RealmInstant]. It is serialized as a [BsonDateTime], to allow direct
 * usage on Mongodb function calls, and deserialized as an unmanaged [RealmInstant].
 *
 * Warning: because [RealmInstant] and [BsonDateTime] have different precision the serialization will
 * lose precision as nanoseconds get truncated to milliseconds.
 *
 * The serializer must be registered per property:
 * ```
 * class Example : RealmObject {
 *     @Serializable(RealmInstantKSerializer::class)
 *     var myInstant: RealmInstant = RealmInstant.now()
 * }
 * ```
 * or per file:
 * ```
 * @file:UseSerializers(RealmInstantKSerializer::class)
 *
 * class Example : RealmObject {
 *     var myInstant: RealmInstant = RealmInstant.now()
 * }
 * ```
 *
 * Adding the following code snippet to a Kotlin file would conveniently register any field using a
 * Realm datatype to its correspondent serializer:
 *
 * ```
 * @file:UseSerializers(
 *     RealmListKSerializer::class,
 *     RealmSetKSerializer::class,
 *     RealmAnyKSerializer::class,
 *     RealmInstantKSerializer::class,
 *     MutableRealmIntKSerializer::class,
 *     RealmUUIDKSerializer::class
 * )
 * ```
 *
 * Serializers for all Realm data types can be found in [io.realm.kotlin.serializers].
 */
public object RealmInstantKSerializer : KSerializer {
    private val serializer = BsonDateTime.serializer()
    override val descriptor: SerialDescriptor = serializer.descriptor

    override fun deserialize(decoder: Decoder): RealmInstant =
        decoder.decodeSerializableValue(serializer).asRealmInstant()

    override fun serialize(encoder: Encoder, value: RealmInstant) {
        encoder.encodeSerializableValue(
            serializer = serializer,
            value = value.asBsonDateTime()
        )
    }
}

/**
 * KSerializer implementation for [RealmAny]. Serialization is done as a specific map structure
 * that represents the a union type with all possible value types:
 *
 * ```
 * realmAny:
 *     type: [INT, BOOL, STRING, BINARY, TIMESTAMP, FLOAT, DOUBLE, DECIMAL128, OBJECT_ID, UUID, OBJECT]
 *     int: Long?
 *     bool: Boolean?
 *     string: String?
 *     binary: ByteArray?
 *     instant: RealmInstant?
 *     float: Float?
 *     double: Double?
 *     decimal128: Decimal128?
 *     objectId: ObjectId?
 *     uuid: RealmUUID?
 *     realmObject: RealmObject?
 * ```
 *
 * Deserialization is done with an unmanaged [RealmAny].
 *
 * The serializer must be registered per property:
 * ```
 * class Example : RealmObject {
 *     @Serializable(RealmAnyKSerializer::class)
 *     var myInstant: RealmAny = RealmAny.create("hello world")
 * }
 * ```
 * or per file:
 * ```
 * @file:UseSerializers(RealmAnyKSerializer::class)
 *
 * class Example : RealmObject {
 *     var myInstant: RealmAny = RealmAny.create("hello world")
 * }
 * ```
 *
 * Serialization of [RealmAny] instances containing [RealmObject] require of a [SerializersModule]
 * mapping such objects to the polymorphic [RealmObject] interface:
 *
 * ```
 * val json = Json {
 *     serializersModule = SerializersModule {
 *         polymorphic(RealmObject::class) {
 *             subclass(SerializableSample::class)
 *         }
 *     }
 * }
 * ```
 *
 * Adding the following code snippet to a Kotlin file would conveniently register any field using a
 * Realm datatype to its correspondent serializer:
 *
 * ```
 * @file:UseSerializers(
 *     RealmListKSerializer::class,
 *     RealmSetKSerializer::class,
 *     RealmAnyKSerializer::class,
 *     RealmInstantKSerializer::class,
 *     MutableRealmIntKSerializer::class,
 *     RealmUUIDKSerializer::class
 * )
 * ```
 *
 * Serializers for all Realm data types can be found in [io.realm.kotlin.serializers].
 */
public object RealmAnyKSerializer : KSerializer {

    /**
     * This class represents a union type of all possible RealmAny types. We cannot write a regular
     * serializer because we need to be able to resolve the serializer for a RealmObject in runtime,
     * and the only way to do it is through kserialization internal functions.
     */
    @Serializable
    private class SerializableRealmAny {
        lateinit var type: String
        var int: Long? = null
        var bool: Boolean? = null
        var string: String? = null
        var binary: ByteArray? = null

        @Serializable(RealmInstantKSerializer::class)
        var instant: RealmInstant? = null
        var float: Float? = null
        var double: Double? = null
        var decimal128: Decimal128? = null
        var objectId: BsonObjectId? = null

        @Serializable(RealmUUIDKSerializer::class)
        var uuid: RealmUUID? = null
        var realmObject: RealmObject? = null

        @Contextual
        var set: RealmSet? = null
        @Contextual
        var list: RealmList? = null
        @Contextual
        var dictionary: RealmDictionary? = null
    }

    private val serializer = SerializableRealmAny.serializer()
    override val descriptor: SerialDescriptor = serializer.descriptor

    @Suppress("ComplexMethod")
    override fun deserialize(decoder: Decoder): RealmAny {
        return decoder.decodeSerializableValue(serializer).let {
            when (Type.valueOf(it.type)) {
                Type.INT -> RealmAny.create(it.int!!.toLong())
                Type.BOOL -> RealmAny.create(it.bool!!)
                Type.STRING -> RealmAny.create(it.string!!)
                Type.BINARY -> RealmAny.create(it.binary!!)
                Type.TIMESTAMP -> RealmAny.create(it.instant!!)
                Type.FLOAT -> RealmAny.create(it.float!!)
                Type.DOUBLE -> RealmAny.create(it.double!!)
                Type.DECIMAL128 -> RealmAny.create(it.decimal128!!)
                Type.OBJECT_ID -> RealmAny.create(it.objectId!!)
                Type.UUID -> RealmAny.create(it.uuid!!)
                Type.OBJECT -> RealmAny.create(it.realmObject!!)
                Type.LIST -> RealmAny.create(it.list!!)
                Type.DICTIONARY -> RealmAny.create(it.dictionary!!)
            }
        }
    }

    @Suppress("ComplexMethod")
    override fun serialize(encoder: Encoder, value: RealmAny) {
        encoder.encodeSerializableValue(
            serializer,
            SerializableRealmAny().apply {
                type = value.type.name
                when (value.type) {
                    Type.INT -> int = value.asLong()
                    Type.BOOL -> bool = value.asBoolean()
                    Type.STRING -> string = value.asString()
                    Type.BINARY -> binary = value.asByteArray()
                    Type.TIMESTAMP -> instant = value.asRealmInstant()
                    Type.FLOAT -> float = value.asFloat()
                    Type.DOUBLE -> double = value.asDouble()
                    Type.DECIMAL128 -> decimal128 = value.asDecimal128()
                    Type.OBJECT_ID -> objectId = BsonObjectId(
                        value.asObjectId().toByteArray()
                    )
                    Type.UUID -> uuid = value.asRealmUUID()
                    Type.OBJECT -> realmObject = value.asRealmObject()
                    Type.LIST -> list = value.asList()
                    Type.DICTIONARY -> dictionary = value.asDictionary()
                }
            }
        )
    }
}

/**
 * KSerializer implementation for [RealmUUID]. Serialized as a [BsonBinary] with subtype
 * [BsonBinarySubType.UUID_STANDARD], and deserialized as an unmanaged [RealmUUID].
 *
 * The serializer must be registered per property:
 * ```
 * class Example : RealmObject {
 *     @Serializable(RealmUUIDKSerializer::class)
 *     var myUUID: RealmUUID = RealmUUID.create()
 * }
 * ```
 * or per file:
 * ```
 * @file:UseSerializers(RealmUUIDKSerializer::class)
 *
 * class Example : RealmObject {
 *     var myUUID: RealmUUID = RealmUUID.create()
 * }
 * ```
 *
 * Adding the following code snippet to a Kotlin file would conveniently register any field using a
 * Realm datatype to its correspondent serializer:
 *
 * ```
 * @file:UseSerializers(
 *     RealmListKSerializer::class,
 *     RealmSetKSerializer::class,
 *     RealmAnyKSerializer::class,
 *     RealmInstantKSerializer::class,
 *     MutableRealmIntKSerializer::class,
 *     RealmUUIDKSerializer::class
 * )
 * ```
 *
 * Serializers for all Realm data types can be found in [io.realm.kotlin.serializers].
 */
public object RealmUUIDKSerializer : KSerializer {
    private val serializer = BsonBinary.serializer()
    override val descriptor: SerialDescriptor = serializer.descriptor

    override fun deserialize(decoder: Decoder): RealmUUID =
        RealmUUID.from(decoder.decodeSerializableValue(serializer).data)

    override fun serialize(encoder: Encoder, value: RealmUUID) {
        encoder.encodeSerializableValue(
            serializer = serializer,
            value = BsonBinary(BsonBinarySubType.UUID_STANDARD, value.bytes)
        )
    }
}

/**
 * KSerializer implementation for [MutableRealmInt]. Serialization is done with a primitive long value,
 * whilst deserialization is done with an unmanaged [MutableRealmInt].
 *
 * The serializer must be registered per property:
 * ```
 * class Example : RealmObject {
 *     @Serializable(MutableRealmIntKSerializer::class)
 *     var myMutableRealmInt: MutableRealmInt = MutableRealmInt.create(0)
 * }
 * ```
 * or per file:
 * ```
 * @file:UseSerializers(MutableRealmIntKSerializer::class)
 *
 * class Example : RealmObject {
 *     var myMutableRealmInt: MutableRealmInt = MutableRealmInt.create(0)
 * }
 * ```
 *
 * Adding the following code snippet to a Kotlin file would conveniently register any field using a
 * Realm datatype to its correspondent serializer:
 *
 * ```
 * @file:UseSerializers(
 *     RealmListKSerializer::class,
 *     RealmSetKSerializer::class,
 *     RealmAnyKSerializer::class,
 *     RealmInstantKSerializer::class,
 *     MutableRealmIntKSerializer::class,
 *     RealmUUIDKSerializer::class
 * )
 * ```
 *
 * Serializers for all Realm data types can be found in [io.realm.kotlin.serializers].
 */
public object MutableRealmIntKSerializer : KSerializer {
    override val descriptor: SerialDescriptor = Long.serializer().descriptor

    override fun deserialize(decoder: Decoder): MutableRealmInt =
        MutableRealmInt.create(decoder.decodeLong())

    override fun serialize(encoder: Encoder, value: MutableRealmInt) {
        encoder.encodeLong(value.toLong())
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy