Skip to content

Commit

Permalink
Support serialization of collections that are not lists
Browse files Browse the repository at this point in the history
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 to serialize generic collections independently of the
concrete implementation.

Fixes Kotlin#1421.
  • Loading branch information
sschuberth committed Jan 9, 2022
1 parent 1175416 commit 96f475e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,17 @@ internal class ReferenceArraySerializer<ElementKlass : Any, Element : ElementKla
}
}

@PublishedApi
internal abstract class CollectionSerializer<E, C: Collection<E>, B>(element: KSerializer<E>) : ListLikeSerializer<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
Expand All @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit 96f475e

Please sign in to comment.