From 6a08e16c23083b815e075410bb4d1a599da1be0d Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Sat, 19 Mar 2022 16:12:19 +0100 Subject: [PATCH 01/14] Done on Android --- .../kotlin/dev/gitlive/firebase/_decoders.kt | 9 ++++--- .../kotlin/dev/gitlive/firebase/_encoders.kt | 24 +++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt index 2b31cece7..92357dda8 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -7,17 +7,20 @@ package dev.gitlive.firebase import kotlinx.serialization.encoding.CompositeDecoder import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind -actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind as StructureKind) { - StructureKind.CLASS, StructureKind.OBJECT -> (value as Map<*, *>).let { map -> +actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = + when(descriptor.kind) { + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map -> FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } } - StructureKind.LIST -> (value as List<*>).let { + StructureKind.LIST, PolymorphicKind.SEALED-> (value as List<*>).let { FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } } StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } \ No newline at end of file diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt index 84576846e..9ce7d8fbb 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -4,18 +4,22 @@ package dev.gitlive.firebase +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.encoding.CompositeEncoder import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind as StructureKind) { - StructureKind.LIST -> mutableListOf() - .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } - StructureKind.MAP -> mutableListOf() - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } - StructureKind.CLASS, StructureKind.OBJECT -> mutableMapOf() - .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } -} \ No newline at end of file +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = + when(descriptor.kind) { + StructureKind.LIST -> mutableListOf() + .also { value = it } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } + StructureKind.MAP -> mutableListOf() + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> mutableMapOf() + .also { value = it } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") + } \ No newline at end of file From 37bf8ca9705894d98fb7efebfa45376b57072119 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Sun, 20 Mar 2022 17:41:52 +0100 Subject: [PATCH 02/14] Added on iOS and js --- .../src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt | 1 - .../src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt | 5 +++-- .../src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt | 5 +++-- .../src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt | 3 ++- .../src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt | 5 +++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt index 9ce7d8fbb..e59feca82 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -4,7 +4,6 @@ package dev.gitlive.firebase -import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.encoding.CompositeEncoder import kotlinx.serialization.descriptors.SerialDescriptor diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt index 05ffad2cd..8da58a48f 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -7,11 +7,12 @@ package dev.gitlive.firebase import kotlinx.serialization.encoding.CompositeDecoder import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind -actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind as StructureKind) { - StructureKind.CLASS, StructureKind.OBJECT -> (value as Map<*, *>).let { map -> +actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind) { + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map -> FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } } StructureKind.LIST -> (value as List<*>).let { diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt index 84576846e..52e6c82dd 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -4,18 +4,19 @@ package dev.gitlive.firebase +import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.encoding.CompositeEncoder import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind as StructureKind) { +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind) { StructureKind.LIST -> mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } StructureKind.MAP -> mutableListOf() .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } - StructureKind.CLASS, StructureKind.OBJECT -> mutableMapOf() + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> mutableMapOf() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } } \ No newline at end of file diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt index 4e0d0b226..496f8670e 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -7,13 +7,14 @@ package dev.gitlive.firebase import kotlinx.serialization.encoding.CompositeDecoder import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.js.Json @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind as StructureKind) { - StructureKind.CLASS, StructureKind.OBJECT -> (value as Json).let { json -> + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Json).let { json -> FirebaseClassDecoder(decodeDouble, js("Object").keys(value).length as Int, { json[it] != undefined }) { desc, index -> json[desc.getElementName(index)] } diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt index 5a7ce7a0f..cfa8e7ff0 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -4,12 +4,13 @@ package dev.gitlive.firebase +import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.encoding.CompositeEncoder import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.js.json -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind as StructureKind) { +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind) { StructureKind.LIST -> Array(descriptor.elementsCount) { null } .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[index] = value } } @@ -19,7 +20,7 @@ actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): Compo value = map FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> if(index % 2 == 0) lastKey = value as String else map[lastKey] = value } } - StructureKind.CLASS, StructureKind.OBJECT -> json() + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> json() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } } From 983702f4e629a13453097d9b914aec8b623c28e6 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Sun, 20 Mar 2022 17:45:43 +0100 Subject: [PATCH 03/14] Add else branches --- .../src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt | 1 + .../src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt | 1 + .../src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt | 3 ++- .../src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt index 8da58a48f..949d9ce8f 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -21,4 +21,5 @@ actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decode StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt index 52e6c82dd..0070916c8 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -19,4 +19,5 @@ actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): Compo StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> mutableMapOf() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } \ No newline at end of file diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt index 496f8670e..484096833 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -13,7 +13,7 @@ import kotlinx.serialization.descriptors.StructureKind import kotlin.js.Json @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") -actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind as StructureKind) { +actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind) { StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Json).let { json -> FirebaseClassDecoder(decodeDouble, js("Object").keys(value).length as Int, { json[it] != undefined }) { desc, index -> json[desc.getElementName(index)] @@ -25,4 +25,5 @@ actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decode StructureKind.MAP -> (js("Object").entries(value) as Array>).let { FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) get(0) else get(1) } } } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt index cfa8e7ff0..a803e39c7 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -23,5 +23,6 @@ actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): Compo StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> json() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } From 5d6edd56d8be20131e5b7a647cc2ce22c2650073 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Sun, 20 Mar 2022 17:46:31 +0100 Subject: [PATCH 04/14] Keep format --- .../kotlin/dev/gitlive/firebase/_decoders.kt | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt index 92357dda8..ea8656fef 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -11,16 +11,15 @@ import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind -actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = - when(descriptor.kind) { - StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map -> - FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } - } - StructureKind.LIST, PolymorphicKind.SEALED-> (value as List<*>).let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } - } - StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } - } - else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") - } \ No newline at end of file +actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind) { + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map -> + FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } + } + StructureKind.LIST, PolymorphicKind.SEALED-> (value as List<*>).let { + FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } + } + StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { + FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } + } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") +} \ No newline at end of file From de286d56b4cef57dc75fced3374b06346078b1fb Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Sun, 20 Mar 2022 17:47:58 +0100 Subject: [PATCH 05/14] Format --- .../kotlin/dev/gitlive/firebase/_decoders.kt | 22 +++++++++---------- .../kotlin/dev/gitlive/firebase/_encoders.kt | 3 +-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt index ea8656fef..588cef0de 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -12,14 +12,14 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder = when(descriptor.kind) { - StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map -> - FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } - } - StructureKind.LIST, PolymorphicKind.SEALED-> (value as List<*>).let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } - } - StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { - FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } - } - else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") -} \ No newline at end of file + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map -> + FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } + } + StructureKind.LIST, PolymorphicKind.SEALED-> (value as List<*>).let { + FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } + } + StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { + FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } + } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") + } \ No newline at end of file diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt index e59feca82..5761b5a13 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -10,8 +10,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = - when(descriptor.kind) { +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind) { StructureKind.LIST -> mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } From 5e88cc275612fbb7724d5b84eb9689f5d0207656 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Sun, 20 Mar 2022 17:49:10 +0100 Subject: [PATCH 06/14] Format --- .../kotlin/dev/gitlive/firebase/_decoders.kt | 2 +- .../kotlin/dev/gitlive/firebase/_encoders.kt | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt index 588cef0de..d8d2643b4 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -15,7 +15,7 @@ actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decode StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> (value as Map<*, *>).let { map -> FirebaseClassDecoder(decodeDouble, map.size, { map.containsKey(it) }) { desc, index -> map[desc.getElementName(index)] } } - StructureKind.LIST, PolymorphicKind.SEALED-> (value as List<*>).let { + StructureKind.LIST -> (value as List<*>).let { FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index] } } StructureKind.MAP -> (value as Map<*, *>).entries.toList().let { diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt index 5761b5a13..0070916c8 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -11,13 +11,13 @@ import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind) { - StructureKind.LIST -> mutableListOf() - .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } - StructureKind.MAP -> mutableListOf() - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } - StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> mutableMapOf() - .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } - else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") - } \ No newline at end of file + StructureKind.LIST -> mutableListOf() + .also { value = it } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } + StructureKind.MAP -> mutableListOf() + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } + StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> mutableMapOf() + .also { value = it } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") +} \ No newline at end of file From 2bb2f07057d550a44333fe74fe887b7d721db1ff Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Mon, 21 Mar 2022 13:27:32 +0100 Subject: [PATCH 07/14] Add testing --- .../kotlin/dev/gitlive/firebase/_decoders.kt | 2 -- .../dev/gitlive/firebase/EncodersTest.kt | 21 +++++++++++++++++++ .../kotlin/dev/gitlive/firebase/_decoders.kt | 2 -- .../kotlin/dev/gitlive/firebase/_decoders.kt | 2 -- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt index d8d2643b4..cabb99e49 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -5,8 +5,6 @@ package dev.gitlive.firebase import kotlinx.serialization.encoding.CompositeDecoder -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind diff --git a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt index 2d5741835..09baee17a 100644 --- a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt +++ b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt @@ -4,6 +4,7 @@ package dev.gitlive.firebase +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer import kotlin.test.Test @@ -17,6 +18,13 @@ expect fun nativeAssertEquals(expected: Any?, actual: Any?): Unit @Serializable data class TestData(val map: Map, val bool: Boolean = false, val nullableBool: Boolean? = null) +@Serializable +sealed class TestSealed { + @Serializable + @SerialName("child") + data class ChildClass(val map: Map, val bool: Boolean = false): TestSealed() +} + class EncodersTest { @Test fun encodeMap() { @@ -37,6 +45,13 @@ class EncodersTest { nativeAssertEquals(nativeMapOf("map" to nativeMapOf("key" to "value"), "bool" to true, "nullableBool" to true), encoded) } + @Test + fun encodeSealedClass() { + val encoded = encode(TestSealed.serializer(), TestSealed.ChildClass(mapOf("key" to "value"), true), shouldEncodeElementDefault = true) + println(encoded.toString()) + nativeAssertEquals(nativeMapOf("type" to "child", "value" to nativeMapOf("map" to nativeMapOf("key" to "value"), "bool" to true)), encoded) + } + @Test fun decodeObject() { val decoded = decode(TestData.serializer(), nativeMapOf("map" to nativeMapOf("key" to "value"))) @@ -54,4 +69,10 @@ class EncodersTest { val decoded = decode(TestData.serializer(), nativeMapOf("map" to mapOf("key" to "value"), "nullableBool" to null)) assertNull(decoded.nullableBool) } + + @Test + fun decodeSealedClass() { + val decoded = decode(TestSealed.serializer(), nativeMapOf("type" to "child", "value" to nativeMapOf("map" to nativeMapOf("key" to "value"), "bool" to true))) + assertEquals(TestSealed.ChildClass(mapOf("key" to "value"), true), decoded) + } } \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt index 949d9ce8f..9d3ca29c5 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -5,8 +5,6 @@ package dev.gitlive.firebase import kotlinx.serialization.encoding.CompositeDecoder -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt index 484096833..166aef84e 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -5,8 +5,6 @@ package dev.gitlive.firebase import kotlinx.serialization.encoding.CompositeDecoder -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind From 6f690662e0820b44da0d6fbbe664d60731571b80 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Mon, 21 Mar 2022 16:14:29 +0100 Subject: [PATCH 08/14] Remove logging --- .../src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt index 09baee17a..944cc6d26 100644 --- a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt +++ b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt @@ -48,7 +48,6 @@ class EncodersTest { @Test fun encodeSealedClass() { val encoded = encode(TestSealed.serializer(), TestSealed.ChildClass(mapOf("key" to "value"), true), shouldEncodeElementDefault = true) - println(encoded.toString()) nativeAssertEquals(nativeMapOf("type" to "child", "value" to nativeMapOf("map" to nativeMapOf("key" to "value"), "bool" to true)), encoded) } From 7a6d8154820bee29cb7b70550d2dca988a945adb Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Thu, 24 Mar 2022 14:30:23 +0100 Subject: [PATCH 09/14] Add polymorphic serialization --- .../kotlin/dev/gitlive/firebase/_decoders.kt | 5 +- .../kotlin/dev/gitlive/firebase/_encoders.kt | 13 +-- .../firebase/FirebaseClassDiscriminator.kt | 7 ++ .../dev/gitlive/firebase/Polymorphic.kt | 87 +++++++++++++++++++ .../kotlin/dev/gitlive/firebase/decoders.kt | 10 ++- .../kotlin/dev/gitlive/firebase/encoders.kt | 28 ++++-- .../dev/gitlive/firebase/EncodersTest.kt | 5 +- .../kotlin/dev/gitlive/firebase/_decoders.kt | 5 +- .../kotlin/dev/gitlive/firebase/_encoders.kt | 9 +- .../kotlin/dev/gitlive/firebase/_decoders.kt | 4 + .../kotlin/dev/gitlive/firebase/_encoders.kt | 11 ++- 11 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseClassDiscriminator.kt create mode 100644 firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt index cabb99e49..4516eabbe 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -20,4 +20,7 @@ actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decode FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } } else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") - } \ No newline at end of file + } + +actual fun getPolymorphicType(value: Any?, discriminator: String): String = + (value as Map<*,*>)[discriminator] as String \ No newline at end of file diff --git a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt index 0070916c8..928936d38 100644 --- a/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/androidMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -4,20 +4,23 @@ package dev.gitlive.firebase -import kotlinx.serialization.descriptors.PolymorphicKind -import kotlinx.serialization.encoding.CompositeEncoder import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind) { +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) { StructureKind.LIST -> mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } StructureKind.MAP -> mutableListOf() .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } - StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> mutableMapOf() + StructureKind.CLASS, StructureKind.OBJECT -> mutableMapOf() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, + setPolymorphicType = { discriminator, type -> + it[discriminator] = type + }, + set = { _, index, value -> it[descriptor.getElementName(index)] = value } + ) } else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } \ No newline at end of file diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseClassDiscriminator.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseClassDiscriminator.kt new file mode 100644 index 000000000..2ed4bafa1 --- /dev/null +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseClassDiscriminator.kt @@ -0,0 +1,7 @@ +package dev.gitlive.firebase + +import kotlinx.serialization.InheritableSerialInfo + +@InheritableSerialInfo +@Target(AnnotationTarget.CLASS) +annotation class FirebaseClassDiscriminator(val discriminator: String) \ No newline at end of file diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt new file mode 100644 index 000000000..e6955ddb4 --- /dev/null +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt @@ -0,0 +1,87 @@ +package dev.gitlive.firebase + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.CompositeDecoder +import kotlinx.serialization.findPolymorphicSerializer +import kotlinx.serialization.internal.AbstractPolymorphicSerializer + + +@Suppress("UNCHECKED_CAST") +internal fun FirebaseEncoder.encodePolymorphically( + serializer: SerializationStrategy, + value: T, + ifPolymorphic: (String) -> Unit +) { + if (serializer !is AbstractPolymorphicSerializer<*>) { + serializer.serialize(this, value) + return + } + val casted = serializer as AbstractPolymorphicSerializer + val baseClassDiscriminator = serializer.descriptor.classDiscriminator() + val actualSerializer = casted.findPolymorphicSerializer(this, value as Any) +// validateIfSealed(casted, actualSerializer, baseClassDiscriminator) +// checkKind(actualSerializer.descriptor.kind) + ifPolymorphic(baseClassDiscriminator) + actualSerializer.serialize(this, value) +} + + + +@Suppress("UNCHECKED_CAST") +internal fun FirebaseDecoder.decodeSerializableValuePolymorphic( + value: Any?, + decodeDouble: (value: Any?) -> Double?, + deserializer: DeserializationStrategy, +): T { + if (deserializer !is AbstractPolymorphicSerializer<*>) { + return deserializer.deserialize(this) + } + + val casted = deserializer as AbstractPolymorphicSerializer + val discriminator = deserializer.descriptor.classDiscriminator() + val type = getPolymorphicType(value, discriminator) + val actualDeserializer = casted.findPolymorphicSerializerOrNull( + structureDecoder(deserializer.descriptor, decodeDouble), + type + ) as DeserializationStrategy + return actualDeserializer.deserialize(this) +} + + +//private fun validateIfSealed( +// serializer: SerializationStrategy<*>, +// actualSerializer: SerializationStrategy, +// classDiscriminator: String +//) { +// if (serializer !is SealedClassSerializer<*>) return +// @Suppress("DEPRECATION_ERROR") +// if (classDiscriminator in actualSerializer.descriptor.jsonCachedSerialNames()) { +// val baseName = serializer.descriptor.serialName +// val actualName = actualSerializer.descriptor.serialName +// error( +// "Sealed class '$actualName' cannot be serialized as base class '$baseName' because" + +// " it has property name that conflicts with class discriminator '$classDiscriminator'. " + +// "You can either change class discriminator with FirebaseClassDiscriminator annotation or " + +// "rename property with @SerialName annotation" +// ) +// } +//} + +//internal fun checkKind(kind: SerialKind) { +// if (kind is SerialKind.ENUM) error("Enums cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead") +// if (kind is PrimitiveKind) error("Primitives cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead") +// if (kind is PolymorphicKind) error("Actual serializer for polymorphic cannot be polymorphic itself") +//} + +internal fun SerialDescriptor.classDiscriminator(): String { + // Plain loop is faster than allocation of Sequence or ArrayList + // We can rely on the fact that only one FirebaseClassDiscriminator is present — + // compiler plugin checked that. + for (annotation in annotations) { + if (annotation is FirebaseClassDiscriminator) return annotation.discriminator + } + return "type" +} + diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt index c771d63e1..6be266b56 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/decoders.kt @@ -25,8 +25,8 @@ fun decode(strategy: DeserializationStrategy, value: Any?, decodeDouble: require(value != null || strategy.descriptor.isNullable) { "Value was null for non-nullable type ${strategy.descriptor.serialName}" } return FirebaseDecoder(value, decodeDouble).decodeSerializableValue(strategy) } - expect fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decodeDouble: (value: Any?) -> Double?): CompositeDecoder +expect fun getPolymorphicType(value: Any?, discriminator: String): String class FirebaseDecoder(internal val value: Any?, private val decodeDouble: (value: Any?) -> Double?) : Decoder { @@ -59,8 +59,11 @@ class FirebaseDecoder(internal val value: Any?, private val decodeDouble: (value override fun decodeNull() = decodeNull(value) - @ExperimentalSerializationApi override fun decodeInline(inlineDescriptor: SerialDescriptor) = FirebaseDecoder(value, decodeDouble) + + override fun decodeSerializableValue(deserializer: DeserializationStrategy): T { + return decodeSerializableValuePolymorphic(value, decodeDouble, deserializer) + } } class FirebaseClassDecoder( @@ -80,7 +83,7 @@ class FirebaseClassDecoder( ?: DECODE_DONE } -open class FirebaseCompositeDecoder constructor( +open class FirebaseCompositeDecoder( private val decodeDouble: (value: Any?) -> Double?, private val size: Int, private val get: (descriptor: SerialDescriptor, index: Int) -> Any? @@ -134,6 +137,7 @@ open class FirebaseCompositeDecoder constructor( @ExperimentalSerializationApi override fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder = FirebaseDecoder(get(descriptor, index), decodeDouble) + } private fun decodeString(value: Any?) = value.toString() diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt index afb637492..7afe032bc 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/encoders.kt @@ -16,14 +16,23 @@ inline fun encode(value: T, shouldEncodeElementDefault: Boolean, pos FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity).apply { encodeSerializableValue(it.firebaseSerializer(), it) }.value } -expect fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder +expect fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder class FirebaseEncoder(internal val shouldEncodeElementDefault: Boolean, positiveInfinity: Any) : TimestampEncoder(positiveInfinity), Encoder { var value: Any? = null override val serializersModule = EmptySerializersModule - override fun beginStructure(descriptor: SerialDescriptor) = structureEncoder(descriptor) + private var polymorphicDiscriminator: String? = null + + override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder { + val encoder = structureEncoder(descriptor) + if (polymorphicDiscriminator != null) { + encoder.encodePolymorphicClassDiscriminator(polymorphicDiscriminator!!, descriptor.serialName) + polymorphicDiscriminator = null + } + return encoder + } override fun encodeBoolean(value: Boolean) { this.value = value @@ -73,9 +82,14 @@ class FirebaseEncoder(internal val shouldEncodeElementDefault: Boolean, positive this.value = value } - @ExperimentalSerializationApi override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder = FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity) + + override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) { + encodePolymorphically(serializer, value) { + polymorphicDiscriminator = it + } + } } abstract class TimestampEncoder(internal val positiveInfinity: Any) { @@ -89,7 +103,8 @@ open class FirebaseCompositeEncoder constructor( private val shouldEncodeElementDefault: Boolean, positiveInfinity: Any, private val end: () -> Unit = {}, - private val set: (descriptor: SerialDescriptor, index: Int, value: Any?) -> Unit + private val setPolymorphicType: (String, String) -> Unit = { _, _ -> }, + private val set: (descriptor: SerialDescriptor, index: Int, value: Any?) -> Unit, ): TimestampEncoder(positiveInfinity), CompositeEncoder { override val serializersModule = EmptySerializersModule @@ -153,6 +168,9 @@ open class FirebaseCompositeEncoder constructor( @ExperimentalSerializationApi override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder = FirebaseEncoder(shouldEncodeElementDefault, positiveInfinity) -} + fun encodePolymorphicClassDiscriminator(discriminator: String, type: String) { + setPolymorphicType(discriminator, type) + } +} diff --git a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt index 944cc6d26..924564fb5 100644 --- a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt +++ b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt @@ -7,6 +7,7 @@ package dev.gitlive.firebase import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.json.JsonClassDiscriminator import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull @@ -48,7 +49,7 @@ class EncodersTest { @Test fun encodeSealedClass() { val encoded = encode(TestSealed.serializer(), TestSealed.ChildClass(mapOf("key" to "value"), true), shouldEncodeElementDefault = true) - nativeAssertEquals(nativeMapOf("type" to "child", "value" to nativeMapOf("map" to nativeMapOf("key" to "value"), "bool" to true)), encoded) + nativeAssertEquals(nativeMapOf("type" to "child", "map" to nativeMapOf("key" to "value"), "bool" to true), encoded) } @Test @@ -71,7 +72,7 @@ class EncodersTest { @Test fun decodeSealedClass() { - val decoded = decode(TestSealed.serializer(), nativeMapOf("type" to "child", "value" to nativeMapOf("map" to nativeMapOf("key" to "value"), "bool" to true))) + val decoded = decode(TestSealed.serializer(), nativeMapOf("type" to "child", "map" to nativeMapOf("key" to "value"), "bool" to true)) assertEquals(TestSealed.ChildClass(mapOf("key" to "value"), true), decoded) } } \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt index 9d3ca29c5..5e87c2f9a 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -20,4 +20,7 @@ actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decode FirebaseCompositeDecoder(decodeDouble, it.size) { _, index -> it[index/2].run { if(index % 2 == 0) key else value } } } else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") -} \ No newline at end of file +} + +actual fun getPolymorphicType(value: Any?, discriminator: String): String = + (value as Map<*,*>)[discriminator] as String \ No newline at end of file diff --git a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt index 0070916c8..e887f1957 100644 --- a/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/iosMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind) { +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) { StructureKind.LIST -> mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it.add(index, value) } } @@ -18,6 +18,11 @@ actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): Compo .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> mutableMapOf() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity, + setPolymorphicType = { discriminator, type -> + it[discriminator] = type + }, + set = { _, index, value -> it[descriptor.getElementName(index)] = value } + ) } else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } \ No newline at end of file diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt index 166aef84e..af200fc69 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_decoders.kt @@ -25,3 +25,7 @@ actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, decode } else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } + +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") +actual fun getPolymorphicType(value: Any?, discriminator: String): String = + (value as Json)[discriminator] as String diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt index a803e39c7..b5f5aeaef 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/_encoders.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.js.json -actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): CompositeEncoder = when(descriptor.kind) { +actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when(descriptor.kind) { StructureKind.LIST -> Array(descriptor.elementsCount) { null } .also { value = it } .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[index] = value } } @@ -22,7 +22,14 @@ actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): Compo } StructureKind.CLASS, StructureKind.OBJECT, PolymorphicKind.SEALED -> json() .also { value = it } - .let { FirebaseCompositeEncoder(shouldEncodeElementDefault, positiveInfinity) { _, index, value -> it[descriptor.getElementName(index)] = value } } + .let { FirebaseCompositeEncoder( + shouldEncodeElementDefault, + positiveInfinity, + setPolymorphicType = { discriminator, type -> + it[discriminator] = type + }, + set = { _, index, value -> it[descriptor.getElementName(index)] = value } + ) } else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } From 477a10eff7b79af4a7b2e18dbfe169ab49365d85 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Thu, 24 Mar 2022 14:52:43 +0100 Subject: [PATCH 10/14] Cleanup --- .../dev/gitlive/firebase/Polymorphic.kt | 32 ------------------- .../dev/gitlive/firebase/EncodersTest.kt | 1 - 2 files changed, 33 deletions(-) diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt index e6955ddb4..ace429795 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt @@ -3,11 +3,9 @@ package dev.gitlive.firebase import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.CompositeDecoder import kotlinx.serialization.findPolymorphicSerializer import kotlinx.serialization.internal.AbstractPolymorphicSerializer - @Suppress("UNCHECKED_CAST") internal fun FirebaseEncoder.encodePolymorphically( serializer: SerializationStrategy, @@ -21,14 +19,10 @@ internal fun FirebaseEncoder.encodePolymorphically( val casted = serializer as AbstractPolymorphicSerializer val baseClassDiscriminator = serializer.descriptor.classDiscriminator() val actualSerializer = casted.findPolymorphicSerializer(this, value as Any) -// validateIfSealed(casted, actualSerializer, baseClassDiscriminator) -// checkKind(actualSerializer.descriptor.kind) ifPolymorphic(baseClassDiscriminator) actualSerializer.serialize(this, value) } - - @Suppress("UNCHECKED_CAST") internal fun FirebaseDecoder.decodeSerializableValuePolymorphic( value: Any?, @@ -49,32 +43,6 @@ internal fun FirebaseDecoder.decodeSerializableValuePolymorphic( return actualDeserializer.deserialize(this) } - -//private fun validateIfSealed( -// serializer: SerializationStrategy<*>, -// actualSerializer: SerializationStrategy, -// classDiscriminator: String -//) { -// if (serializer !is SealedClassSerializer<*>) return -// @Suppress("DEPRECATION_ERROR") -// if (classDiscriminator in actualSerializer.descriptor.jsonCachedSerialNames()) { -// val baseName = serializer.descriptor.serialName -// val actualName = actualSerializer.descriptor.serialName -// error( -// "Sealed class '$actualName' cannot be serialized as base class '$baseName' because" + -// " it has property name that conflicts with class discriminator '$classDiscriminator'. " + -// "You can either change class discriminator with FirebaseClassDiscriminator annotation or " + -// "rename property with @SerialName annotation" -// ) -// } -//} - -//internal fun checkKind(kind: SerialKind) { -// if (kind is SerialKind.ENUM) error("Enums cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead") -// if (kind is PrimitiveKind) error("Primitives cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead") -// if (kind is PolymorphicKind) error("Actual serializer for polymorphic cannot be polymorphic itself") -//} - internal fun SerialDescriptor.classDiscriminator(): String { // Plain loop is faster than allocation of Sequence or ArrayList // We can rely on the fact that only one FirebaseClassDiscriminator is present — diff --git a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt index 924564fb5..d61c1e5d1 100644 --- a/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt +++ b/firebase-common/src/commonTest/kotlin/dev/gitlive/firebase/EncodersTest.kt @@ -7,7 +7,6 @@ package dev.gitlive.firebase import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer -import kotlinx.serialization.json.JsonClassDiscriminator import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull From ae01585d2c21ec08ee8eee6b0f96613cbac4ca8e Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Thu, 24 Mar 2022 19:43:10 +0100 Subject: [PATCH 11/14] Add polymorphic serialization section to Readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 26101e39d..802f01b0c 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,12 @@ data class Post( ``` +

Polymorphic serialization (sealed classes)

+ +This sdk will handle polymorphic serialization automatically if you have a sealed class and its children marked as `Serializable`. It will include a `type` property that will be used to discriminate which child class is the serialized. + +You can change this `type` property by using the `@FirebaseClassDiscrminator` annotation in the parent sealed class. +

Default arguments

To reduce boilerplate, default arguments are used in the places where the Firebase Android SDK employs the builder pattern: From 2516bebd78ffb305b3aaa9c42b52acbecca548e4 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Fri, 25 Mar 2022 22:49:44 +0100 Subject: [PATCH 12/14] Add sample of @FirebaseClassDiscriminator usage --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 802f01b0c..b5b2a490b 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,28 @@ data class Post( This sdk will handle polymorphic serialization automatically if you have a sealed class and its children marked as `Serializable`. It will include a `type` property that will be used to discriminate which child class is the serialized. -You can change this `type` property by using the `@FirebaseClassDiscrminator` annotation in the parent sealed class. +You can change this `type` property by using the `@FirebaseClassDiscrminator` annotation in the parent sealed class: + +```kotlin +@Serializable +@FirebaseClassDiscriminator("class") +sealed class Parent { + @Serializable + @SerialName("child") + data class Child( + val property: Boolean + ) : Parent +} +``` + +In combination with a `SerialName` specified for the child class, you have full control over the serialized data. In this case it will be: + +```json +{ + class: "child", + property: true +} +```

Default arguments

From a2f6b6854d14543f703dfdf459c647d0c410c4c7 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Fri, 25 Mar 2022 22:50:57 +0100 Subject: [PATCH 13/14] Fix sample --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b5b2a490b..084fbb39d 100644 --- a/README.md +++ b/README.md @@ -124,8 +124,8 @@ In combination with a `SerialName` specified for the child class, you have full ```json { - class: "child", - property: true + "class": "child", + "property": true } ``` From a9a20519a587d9a473ad3a1bb9905d04d9013c98 Mon Sep 17 00:00:00 2001 From: Ignacio Ruiz Date: Wed, 6 Apr 2022 11:10:09 +0200 Subject: [PATCH 14/14] Add link to kotlinx.serialization --- .../src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt index ace429795..aceb6002f 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/Polymorphic.kt @@ -6,6 +6,10 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.findPolymorphicSerializer import kotlinx.serialization.internal.AbstractPolymorphicSerializer +/* + * This code was inspired on polymorphic json serialization of kotlinx.serialization. + * See https://github.com/Kotlin/kotlinx.serialization/blob/master/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt + */ @Suppress("UNCHECKED_CAST") internal fun FirebaseEncoder.encodePolymorphically( serializer: SerializationStrategy,