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

commonMain.dk.cachet.carp.common.infrastructure.serialization.Serialization.kt Maven / Gradle / Ivy

@file:Suppress( "WildcardImport" )

package dk.cachet.carp.common.infrastructure.serialization

import dk.cachet.carp.common.application.data.*
import dk.cachet.carp.common.application.data.input.*
import dk.cachet.carp.common.application.data.input.elements.*
import dk.cachet.carp.common.application.devices.*
import dk.cachet.carp.common.application.sampling.*
import dk.cachet.carp.common.application.tasks.*
import dk.cachet.carp.common.application.triggers.*
import dk.cachet.carp.common.application.users.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.*


/**
 * Types in the [dk.cachet.carp.common] module which need to be registered when using [Json] serializer.
 */
val COMMON_SERIAL_MODULE = SerializersModule {
    // `data` namespace.
    polymorphic( Data::class )
    {
        // DataType classes.
        subclass( Acceleration::class )
        subclass( ECG::class )
        subclass( FreeFormText::class )
        subclass( Geolocation::class )
        subclass( HeartRate::class )
        // HACK: explicit serializer needs to be registered for object declarations due to limitation of the JS legacy backend.
        // https://github.com/Kotlin/kotlinx.serialization/issues/1138#issuecomment-707989920
        // This can likely be removed once we upgrade to the new IR backend.
        subclass( NoData::class, NoData.serializer() )
        subclass( RRInterval::class, RRInterval.serializer() )
        subclass( SignalStrength::class )
        subclass( SensorSkinContact::class )
        subclass( StepCount::class )
        subclass( TriggeredTask::class )

        // InputDataType classes.
        subclass(
            CustomInput::class,
            CustomInputSerializer( String::class, Int::class )
        )
        subclass( Sex::class, PolymorphicEnumSerializer( Sex.serializer() ) )
    }
    polymorphic( InputElement::class )
    {
        subclass( SelectOne::class )
        subclass( Text::class )
    }


    // `devices` namespace.
    fun PolymorphicModuleBuilder.registerMasterDeviceDescriptorSubclasses()
    {
        subclass( CustomProtocolDevice::class )
        subclass( Smartphone::class )

        subclass( CustomMasterDeviceDescriptor::class )
    }
    polymorphic( DeviceDescriptor::class )
    {
        subclass( AltBeacon::class )
        subclass( BLEHeartRateDevice::class )
        registerMasterDeviceDescriptorSubclasses()

        subclass( CustomDeviceDescriptor::class )
        default { DeviceDescriptorSerializer }
    }
    polymorphic( MasterDeviceDescriptor::class )
    {
        registerMasterDeviceDescriptorSubclasses()

        default { MasterDeviceDescriptorSerializer }
    }
    polymorphic( DeviceRegistration::class )
    {
        subclass( AltBeaconDeviceRegistration::class )
        subclass( BLESerialNumberDeviceRegistration::class )
        subclass( DefaultDeviceRegistration::class )
        subclass( MACAddressDeviceRegistration::class )

        subclass( CustomDeviceRegistration::class )
        default { DeviceRegistrationSerializer }
    }


    // `sampling` namespace.
    polymorphic( SamplingConfiguration::class )
    {
        @Suppress( "UNCHECKED_CAST" )
        subclass(
            BatteryAwareSamplingConfiguration::class,
            BatteryAwareSamplingConfiguration.serializer( PolymorphicSerializer( SamplingConfiguration::class ) )
                as KSerializer>
        )
        subclass( GranularitySamplingConfiguration::class )
        subclass( IntervalSamplingConfiguration::class )
        subclass( NoOptionsSamplingConfiguration::class, NoOptionsSamplingConfiguration.serializer() )

        subclass( CustomSamplingConfiguration::class )
        default { SamplingConfigurationSerializer }
    }


    // `tasks` namespace.
    polymorphic( TaskDescriptor::class )
    {
        subclass( BackgroundTask::class )
        subclass( CustomProtocolTask::class )
        subclass( WebTask::class )

        subclass( CustomTaskDescriptor::class )
        default { TaskDescriptorSerializer }
    }


    // `triggers` namespace.
    polymorphic( Trigger::class )
    {
        subclass( ElapsedTimeTrigger::class )
        subclass( ManualTrigger::class )
        subclass( ScheduledTrigger::class )

        subclass( CustomTrigger::class )
        default { TriggerSerializer }
    }


    // `users` namespace.
    polymorphic( AccountIdentity::class )
    {
        subclass( UsernameAccountIdentity::class )
        subclass( EmailAccountIdentity::class )
    }
}

/**
 * Name of the class descriptor property for polymorphic serialization.
 */
const val CLASS_DISCRIMINATOR: String = "\$type"

/**
 * A default CARP infrastructure serializer capable of serializing all [dk.cachet.carp.common] types.
 * In case custom extending types are defined, this variable should be reassigned for serialization extension functions to work as expected.
 * [createDefaultJSON] can be used to this end, by including all extending types in the [SerializersModule] as parameter.
 */
var JSON: Json = createDefaultJSON()


/**
 * Create a [Json] serializer adopting a default CARP infrastructure configuration.
 * This ensures a global configuration on how serialization should occur.
 * Additional types the serializer needs to be aware about (such as polymorph extending classes) should be registered through [module].
 */
fun createDefaultJSON( module: SerializersModule? = null ): Json
{
    val jsonSerializersModule = if ( module == null ) COMMON_SERIAL_MODULE else COMMON_SERIAL_MODULE + module

    return Json {
        classDiscriminator = CLASS_DISCRIMINATOR
        serializersModule = jsonSerializersModule
        // TODO: `encodeDefaults` changed in kotlinx.serialization 1.0.0-RC2 to false by default
        //  which caused unknown polymorphic serializer tests to fail. Verify whether we need this.
        encodeDefaults = true
    }
}

/**
 * Create a [DeviceRegistration] from JSON, serialized using the globally set infrastructure serializer ([JSON]).
 */
fun DeviceRegistration.Companion.fromJson( json: String ): DeviceRegistration =
    JSON.decodeFromString( serializer(), json )

/**
 * Serialize to JSON, using the globally set infrastructure serializer ([JSON]).
 */
fun DeviceRegistration.toJson(): String =
    JSON.encodeToString( DeviceRegistration.serializer(), this )




© 2015 - 2025 Weber Informatics LLC | Privacy Policy