Skip to content

Commit

Permalink
✨ Added List and Map decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
allinelara committed Mar 13, 2024
1 parent f7b8458 commit da8ba51
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.sipgate.federmappe.firestore
package de.sipgate.federmappe.common

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package de.sipgate.federmappe.common.serializers
import de.sipgate.federmappe.firestore.StringMapToObjectDecoder
import de.sipgate.federmappe.firestore.serializers.UriSerializer
import kotlinx.serialization.Contextual
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.contextual
Expand All @@ -16,6 +17,7 @@ class UriSerializerTest {
private val uriString = "https://sipgate.de:443/some/path?with=multiple&query=args"
private val uri = URI.create(uriString)

@OptIn(ExperimentalSerializationApi::class)
@Test
fun testDeserialization() {
@Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.sipgate.federmappe.firestore

import com.google.firebase.Timestamp
import de.sipgate.federmappe.common.decodeEnum
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
Expand Down Expand Up @@ -30,7 +31,8 @@ class ListDecoder(
else -> index++
}

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeEnum(enumDescriptor, ::decodeValue)
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
decodeEnum(enumDescriptor, ::decodeValue)

override fun decodeNotNullMark(): Boolean =
when {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.sipgate.federmappe.firestore

import android.util.Log
import de.sipgate.federmappe.common.decodeEnum
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
Expand Down Expand Up @@ -30,7 +31,8 @@ class MapDecoder(

override fun decodeValue(): Any = flattenedData[index] ?: throw SerializationException("error decoding")

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeEnum(enumDescriptor, ::decodeValue)
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
decodeEnum(enumDescriptor, ::decodeValue)

override fun decodeNotNullMark(): Boolean = flattenedData[index] != null

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.sipgate.federmappe.firestore

import com.google.firebase.Timestamp
import de.sipgate.federmappe.common.decodeEnum
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
Expand Down Expand Up @@ -46,7 +47,8 @@ class StringMapToObjectDecoder(

override fun decodeNotNullMark(): Boolean = data[key] != null

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeEnum(enumDescriptor, ::decodeValue)
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
decodeEnum(enumDescriptor, ::decodeValue)

@Suppress("UNCHECKED_CAST")
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.sipgate.federmappe.firestore.serializers

import com.google.firebase.Timestamp
import de.sipgate.federmappe.firestore.StringMapToObjectDecoder
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer
import org.junit.jupiter.api.Assertions
Expand All @@ -15,6 +16,7 @@ class DateSerializerTest {
/* Nanos will be ignored, because java.util.Date isn't precise enough. */
private val date = Date( epochSeconds + nanos)

@OptIn(ExperimentalSerializationApi::class)
@Test
fun testDeserialization() {
@Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package de.sipgate.federmappe.realtimedb

import com.google.firebase.database.DataSnapshot
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.AbstractDecoder
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule

@ExperimentalSerializationApi
class ListDecoder(
private val list: ArrayDeque<DataSnapshot>,
private val elementsCount: Int = 0,
override val serializersModule: SerializersModule = EmptySerializersModule(),
) : AbstractDecoder() {
private var index = 0

override fun decodeSequentially(): Boolean = true

override fun decodeCollectionSize(descriptor: SerialDescriptor): Int = elementsCount

override fun decodeValue(): Any = list.removeFirst().value!!

override fun decodeElementIndex(descriptor: SerialDescriptor): Int =
when (index) {
elementsCount -> CompositeDecoder.DECODE_DONE
else -> index++
}

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
decodeEnum(enumDescriptor, ::decodeValue)

override fun decodeNotNullMark(): Boolean =
when {
list.firstOrNull()?.value != null -> true
else -> false.also { list.removeFirst() }
}

@Suppress("UNCHECKED_CAST")
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
val value = list.removeFirst()

when (descriptor.kind) {
StructureKind.CLASS ->
return SnapshotDecoder(
dataSnapshot = value,
ignoreUnknownProperties = true,
serializersModule = this.serializersModule,
)
StructureKind.LIST -> {
val subList = (value as Iterable<DataSnapshot>).toCollection(mutableListOf())
return ListDecoder(ArrayDeque(subList), subList.size, serializersModule)
}
else -> {}
}

return ListDecoder(list, descriptor.elementsCount)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package de.sipgate.federmappe.realtimedb

import android.util.Log
import com.google.firebase.database.DataSnapshot
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.AbstractDecoder
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule

@ExperimentalSerializationApi
class MapDecoder(
private val list: List<DataSnapshot?>,
override val serializersModule: SerializersModule = EmptySerializersModule(),
private val ignoreUnknownProperties: Boolean = false,
) : AbstractDecoder() {
private val keysIterator = list.iterator()
private var index: Int = -2

private val skippedValues = mutableSetOf<String>()

override fun decodeCollectionSize(descriptor: SerialDescriptor): Int = list.size

override fun decodeValue(): Any = list[index]!!.value!!

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
decodeEnum(enumDescriptor, ::decodeValue)

override fun decodeNotNullMark(): Boolean = list[index] != null

override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
while (keysIterator.hasNext()) {
val nextKey = keysIterator.next()
if (index >= 0) {
if (index % 2 == 0) {
index += 1
return index
}
}
val nextIndex =
if (descriptor.kind == StructureKind.MAP) {
list.indexOf(nextKey)
} else {
descriptor.getElementIndex(nextKey!!.key!!)
}
if (nextIndex == CompositeDecoder.UNKNOWN_NAME) {
Log.w("MapDecoder", "encountered unknown key while decoding")
skippedValues.add(nextKey!!.key!!)
continue
}

index = nextIndex
return nextIndex
}

return CompositeDecoder.DECODE_DONE
}

@Suppress("UNCHECKED_CAST")
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
val value =
if (index % 2 == 0) {
list[index + 1]
} else {
list[index]
}!!

when (descriptor.kind) {
StructureKind.CLASS -> {
return SnapshotDecoder(
dataSnapshot = value,
ignoreUnknownProperties = ignoreUnknownProperties,
serializersModule = serializersModule,
)
}
StructureKind.MAP -> {
return MapDecoder(
list = value.children.map { it },
ignoreUnknownProperties = ignoreUnknownProperties,
)
}
StructureKind.LIST -> {
val list = value.children.map { it }
return ListDecoder(ArrayDeque(list), list.size, serializersModule)
}
else -> throw SerializationException("Given value is neither a list nor a type $value")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,29 @@ class SnapshotDecoder(

val value = dataSnapshot.child(key!!)
val valueDescriptor = descriptor.kind
if (valueDescriptor == StructureKind.CLASS) {
return SnapshotDecoder(
dataSnapshot = value,
ignoreUnknownProperties = ignoreUnknownProperties,
serializersModule = this.serializersModule,
when (valueDescriptor) {
StructureKind.CLASS -> {
return SnapshotDecoder(
dataSnapshot = value,
ignoreUnknownProperties = ignoreUnknownProperties,
serializersModule = this.serializersModule,
)
}
StructureKind.MAP -> {
return MapDecoder(
list = value.children.map { it },
ignoreUnknownProperties = ignoreUnknownProperties,
serializersModule = serializersModule,
)
}
StructureKind.LIST -> {
val list = value.children.map { it }
return ListDecoder(ArrayDeque(list), list.size, serializersModule)
}
else -> throw SerializationException(
"Given value is neither a list nor a type! value: $value, type: ${value::class.qualifiedName}"
)
}

throw SerializationException(
"Given value is neither a list nor a type! value: $value, type: ${value::class.qualifiedName}"
)
}

override fun endStructure(descriptor: SerialDescriptor) {
Expand Down

0 comments on commit da8ba51

Please sign in to comment.