summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Schuberth <sschuberth@users.noreply.github.com>2022-01-28 14:30:28 +0100
committerLeonid Startsev <sandwwraith@gmail.com>2022-01-28 16:42:49 +0300
commita27c36594ce4f6ef3a6f869cf38f5fa9ff9bd71e (patch)
tree80cd2ea25d3aab9cac8e0bc23ffe0b2fc81ca5a6
parent2e960d0c35b26ea0f5b3dc460e7a32d34d97aa8e (diff)
downloadkotlinx.serialization-a27c36594ce4f6ef3a6f869cf38f5fa9ff9bd71e.tar.gz
Support serialization of collections that are not lists (#1821)
So far there was an implicit assumption hard-coded that collections are always lists. However, sets are also collections, and can be serialized to JSON arrays just like lists. This change allows serializing generic collections independently of the concrete implementation. Fixes #1421. * CollectionSerializers: Remove redundant visibility modifiers * SealedGenericClassesTest: Remove unused variables to avoid warnings * Rename `ListLikeSerializer` to `CollectionLikeSerializer` Now with `CollectionSerializer` inheriting from `ListLikeSerializer`, it makes sense to rename `ListLikeSerializer` to the more generic `CollectionLikeSerializer`. (cherry picked from commit d254e6d62ee2eb876cf54631f82e131de6a03c34)
-rw-r--r--core/api/kotlinx-serialization-core.api42
-rw-r--r--core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt35
-rw-r--r--core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt8
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt41
4 files changed, 85 insertions, 41 deletions
diff --git a/core/api/kotlinx-serialization-core.api b/core/api/kotlinx-serialization-core.api
index 9159db42..4aab661b 100644
--- a/core/api/kotlinx-serialization-core.api
+++ b/core/api/kotlinx-serialization-core.api
@@ -542,13 +542,11 @@ public abstract class kotlinx/serialization/internal/AbstractPolymorphicSerializ
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
}
-public final class kotlinx/serialization/internal/ArrayListSerializer : kotlinx/serialization/internal/ListLikeSerializer {
+public final class kotlinx/serialization/internal/ArrayListSerializer : kotlinx/serialization/internal/CollectionSerializer {
public fun <init> (Lkotlinx/serialization/KSerializer;)V
public synthetic fun builder ()Ljava/lang/Object;
public synthetic fun builderSize (Ljava/lang/Object;)I
public synthetic fun checkCapacity (Ljava/lang/Object;I)V
- public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
- public synthetic fun collectionSize (Ljava/lang/Object;)I
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
@@ -624,6 +622,23 @@ public final class kotlinx/serialization/internal/CharSerializer : kotlinx/seria
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
}
+public abstract class kotlinx/serialization/internal/CollectionLikeSerializer : kotlinx/serialization/internal/AbstractCollectionSerializer {
+ public synthetic fun <init> (Lkotlinx/serialization/KSerializer;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public abstract fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ protected abstract fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
+ protected final fun readAll (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/Object;II)V
+ protected fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+}
+
+public abstract class kotlinx/serialization/internal/CollectionSerializer : kotlinx/serialization/internal/CollectionLikeSerializer {
+ public fun <init> (Lkotlinx/serialization/KSerializer;)V
+ public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
+ protected fun collectionIterator (Ljava/util/Collection;)Ljava/util/Iterator;
+ public synthetic fun collectionSize (Ljava/lang/Object;)I
+ protected fun collectionSize (Ljava/util/Collection;)I
+}
+
public final class kotlinx/serialization/internal/DoubleArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
}
@@ -717,13 +732,11 @@ public final class kotlinx/serialization/internal/HashMapSerializer : kotlinx/se
public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
}
-public final class kotlinx/serialization/internal/HashSetSerializer : kotlinx/serialization/internal/ListLikeSerializer {
+public final class kotlinx/serialization/internal/HashSetSerializer : kotlinx/serialization/internal/CollectionSerializer {
public fun <init> (Lkotlinx/serialization/KSerializer;)V
public synthetic fun builder ()Ljava/lang/Object;
public synthetic fun builderSize (Ljava/lang/Object;)I
public synthetic fun checkCapacity (Ljava/lang/Object;I)V
- public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
- public synthetic fun collectionSize (Ljava/lang/Object;)I
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
@@ -788,28 +801,17 @@ public final class kotlinx/serialization/internal/LinkedHashMapSerializer : kotl
public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
}
-public final class kotlinx/serialization/internal/LinkedHashSetSerializer : kotlinx/serialization/internal/ListLikeSerializer {
+public final class kotlinx/serialization/internal/LinkedHashSetSerializer : kotlinx/serialization/internal/CollectionSerializer {
public fun <init> (Lkotlinx/serialization/KSerializer;)V
public synthetic fun builder ()Ljava/lang/Object;
public synthetic fun builderSize (Ljava/lang/Object;)I
public synthetic fun checkCapacity (Ljava/lang/Object;I)V
- public synthetic fun collectionIterator (Ljava/lang/Object;)Ljava/util/Iterator;
- public synthetic fun collectionSize (Ljava/lang/Object;)I
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
public synthetic fun toBuilder (Ljava/lang/Object;)Ljava/lang/Object;
public synthetic fun toResult (Ljava/lang/Object;)Ljava/lang/Object;
}
-public abstract class kotlinx/serialization/internal/ListLikeSerializer : kotlinx/serialization/internal/AbstractCollectionSerializer {
- public synthetic fun <init> (Lkotlinx/serialization/KSerializer;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
- public abstract fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- protected abstract fun insert (Ljava/lang/Object;ILjava/lang/Object;)V
- protected final fun readAll (Lkotlinx/serialization/encoding/CompositeDecoder;Ljava/lang/Object;II)V
- protected fun readElement (Lkotlinx/serialization/encoding/CompositeDecoder;ILjava/lang/Object;Z)V
- public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
-}
-
public final class kotlinx/serialization/internal/LongArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
}
@@ -935,7 +937,7 @@ public class kotlinx/serialization/internal/PluginGeneratedSerialDescriptor : ko
public abstract class kotlinx/serialization/internal/PrimitiveArrayBuilder {
}
-public abstract class kotlinx/serialization/internal/PrimitiveArraySerializer : kotlinx/serialization/internal/ListLikeSerializer {
+public abstract class kotlinx/serialization/internal/PrimitiveArraySerializer : kotlinx/serialization/internal/CollectionLikeSerializer {
public synthetic fun builder ()Ljava/lang/Object;
protected final fun builder ()Lkotlinx/serialization/internal/PrimitiveArrayBuilder;
public synthetic fun builderSize (Ljava/lang/Object;)I
@@ -955,7 +957,7 @@ public abstract class kotlinx/serialization/internal/PrimitiveArraySerializer :
protected abstract fun writeContent (Lkotlinx/serialization/encoding/CompositeEncoder;Ljava/lang/Object;I)V
}
-public final class kotlinx/serialization/internal/ReferenceArraySerializer : kotlinx/serialization/internal/ListLikeSerializer {
+public final class kotlinx/serialization/internal/ReferenceArraySerializer : kotlinx/serialization/internal/CollectionLikeSerializer {
public fun <init> (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
public synthetic fun builder ()Ljava/lang/Object;
public synthetic fun builderSize (Ljava/lang/Object;)I
diff --git a/core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt b/core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt
index fbd16c58..fcf4cfa7 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/CollectionSerializers.kt
@@ -54,7 +54,7 @@ public sealed class AbstractCollectionSerializer<Element, Collection, Builder> :
}
@PublishedApi
-internal sealed class ListLikeSerializer<Element, Collection, Builder>(
+internal sealed class CollectionLikeSerializer<Element, Collection, Builder>(
private val elementSerializer: KSerializer<Element>
) : AbstractCollectionSerializer<Element, Collection, Builder>() {
@@ -70,13 +70,13 @@ internal sealed class ListLikeSerializer<Element, Collection, Builder>(
}
}
- protected final override fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int) {
+ final override fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int) {
require(size >= 0) { "Size must be known in advance when using READ_ALL" }
for (index in 0 until size)
readElement(decoder, startIndex + index, builder, checkIndex = false)
}
- protected override fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean) {
+ override fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean) {
builder.insert(index, decoder.decodeSerializableElement(descriptor, index, elementSerializer))
}
}
@@ -143,7 +143,7 @@ internal abstract class PrimitiveArrayBuilder<Array> internal constructor() {
internal abstract class PrimitiveArraySerializer<Element, Array, Builder
: PrimitiveArrayBuilder<Array>> internal constructor(
primitiveSerializer: KSerializer<Element>
-) : ListLikeSerializer<Element, Array, Builder>(primitiveSerializer) {
+) : CollectionLikeSerializer<Element, Array, Builder>(primitiveSerializer) {
final override val descriptor: SerialDescriptor = PrimitiveArrayDescriptor(primitiveSerializer.descriptor)
final override fun Builder.builderSize(): Int = position
@@ -160,7 +160,7 @@ internal abstract class PrimitiveArraySerializer<Element, Array, Builder
protected abstract fun empty(): Array
- protected abstract override fun readElement(
+ abstract override fun readElement(
decoder: CompositeDecoder,
index: Int,
builder: Builder,
@@ -184,7 +184,7 @@ internal abstract class PrimitiveArraySerializer<Element, Array, Builder
internal class ReferenceArraySerializer<ElementKlass : Any, Element : ElementKlass?>(
private val kClass: KClass<ElementKlass>,
eSerializer: KSerializer<Element>
-) : ListLikeSerializer<Element, Array<Element>, ArrayList<Element>>(eSerializer) {
+) : CollectionLikeSerializer<Element, Array<Element>, ArrayList<Element>>(eSerializer) {
override val descriptor: SerialDescriptor = ArrayClassDesc(eSerializer.descriptor)
override fun Array<Element>.collectionSize(): Int = size
@@ -202,12 +202,17 @@ internal class ReferenceArraySerializer<ElementKlass : Any, Element : ElementKla
}
}
+@PublishedApi
+internal abstract class CollectionSerializer<E, C: Collection<E>, B>(element: KSerializer<E>) : CollectionLikeSerializer<E, C, B>(element) {
+ override fun C.collectionSize(): Int = size
+ override fun C.collectionIterator(): Iterator<E> = iterator()
+}
+
@InternalSerializationApi
@PublishedApi
-internal class ArrayListSerializer<E>(element: KSerializer<E>) : ListLikeSerializer<E, List<E>, ArrayList<E>>(element) {
+internal class ArrayListSerializer<E>(element: KSerializer<E>) : CollectionSerializer<E, List<E>, ArrayList<E>>(element) {
override val descriptor: SerialDescriptor = ArrayListClassDesc(element.descriptor)
- override fun List<E>.collectionSize(): Int = size
- override fun List<E>.collectionIterator(): Iterator<E> = iterator()
+
override fun builder(): ArrayList<E> = arrayListOf()
override fun ArrayList<E>.builderSize(): Int = size
override fun ArrayList<E>.toResult(): List<E> = this
@@ -219,11 +224,9 @@ internal class ArrayListSerializer<E>(element: KSerializer<E>) : ListLikeSeriali
@PublishedApi
internal class LinkedHashSetSerializer<E>(
eSerializer: KSerializer<E>
-) : ListLikeSerializer<E, Set<E>, LinkedHashSet<E>>(eSerializer) {
-
+) : CollectionSerializer<E, Set<E>, LinkedHashSet<E>>(eSerializer) {
override val descriptor: SerialDescriptor = LinkedHashSetClassDesc(eSerializer.descriptor)
- override fun Set<E>.collectionSize(): Int = size
- override fun Set<E>.collectionIterator(): Iterator<E> = iterator()
+
override fun builder(): LinkedHashSet<E> = linkedSetOf()
override fun LinkedHashSet<E>.builderSize(): Int = size
override fun LinkedHashSet<E>.toResult(): Set<E> = this
@@ -235,11 +238,9 @@ internal class LinkedHashSetSerializer<E>(
@PublishedApi
internal class HashSetSerializer<E>(
eSerializer: KSerializer<E>
-) : ListLikeSerializer<E, Set<E>, HashSet<E>>(eSerializer) {
-
+) : CollectionSerializer<E, Set<E>, HashSet<E>>(eSerializer) {
override val descriptor: SerialDescriptor = HashSetClassDesc(eSerializer.descriptor)
- override fun Set<E>.collectionSize(): Int = size
- override fun Set<E>.collectionIterator(): Iterator<E> = iterator()
+
override fun builder(): HashSet<E> = HashSet()
override fun HashSet<E>.builderSize(): Int = size
override fun HashSet<E>.toResult(): Set<E> = this
diff --git a/core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt b/core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt
index 150ab7a7..7840cd23 100644
--- a/core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt
+++ b/core/commonTest/src/kotlinx/serialization/SealedGenericClassesTest.kt
@@ -31,13 +31,13 @@ class SealedGenericClassesTest {
// Test that compilation and retrieval is successful
@Test
fun testQuery() {
- val serial1 = Query.SimpleQuery.serializer(String.serializer())
- val serial2 = Query.serializer(UnitSerializer)
+ Query.SimpleQuery.serializer(String.serializer())
+ Query.serializer(UnitSerializer)
}
@Test
fun testFetcher() {
- val serial1 = Fetcher.SomethingFetcher.serializer()
- val serial2 = Fetcher.serializer(Something.serializer())
+ Fetcher.SomethingFetcher.serializer()
+ Fetcher.serializer(Something.serializer())
}
}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt
new file mode 100644
index 00000000..ca8116a0
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class CollectionSerializerTest {
+
+ @Serializable
+ data class CollectionWrapper(
+ val collection: Collection<String>
+ )
+
+ @Test
+ fun testListJson() {
+ val list = listOf("foo", "bar", "foo", "bar")
+
+ val string = Json.encodeToString(CollectionWrapper(list))
+ assertEquals("""{"collection":["foo","bar","foo","bar"]}""", string)
+
+ val wrapper = Json.decodeFromString<CollectionWrapper>(string)
+ assertEquals(list, wrapper.collection)
+ }
+
+ @Test
+ fun testSetJson() {
+ val set = setOf("foo", "bar", "foo", "bar")
+
+ val string = Json.encodeToString(CollectionWrapper(set))
+ assertEquals("""{"collection":["foo","bar"]}""", string)
+
+ val wrapper = Json.decodeFromString<CollectionWrapper>(string)
+ assertEquals(set.toList(), wrapper.collection)
+ }
+}