-
Notifications
You must be signed in to change notification settings - Fork 620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation for automatically registering sealed serializers in polymorphic modules #2201
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,15 +19,38 @@ public class PolymorphicModuleBuilder<in Base : Any> @PublishedApi internal cons | |
private val baseClass: KClass<Base>, | ||
private val baseSerializer: KSerializer<Base>? = null | ||
) { | ||
private val subclasses: MutableList<Pair<KClass<out Base>, KSerializer<out Base>>> = mutableListOf() | ||
private val subclasses: MutableMap<KClass<out Base>, KSerializer<out Base>> = mutableMapOf() | ||
private var defaultSerializerProvider: ((Base) -> SerializationStrategy<Base>?)? = null | ||
private var defaultDeserializerProvider: ((String?) -> DeserializationStrategy<Base>?)? = null | ||
|
||
|
||
/** | ||
* Registers the child serializers for the sealed [subclass] [serializer] in the resulting module under the [base class][Base]. | ||
*/ | ||
public inline fun <reified T : Base> subclassesOf(): Unit = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All new functions should be marked with |
||
subclassesOf(serializer<T>()) | ||
|
||
|
||
/** | ||
* Registers the subclasses of the given class as subclasses of the outer class. This currently requires `baseClass` | ||
* to be sealed. | ||
*/ | ||
public fun <T: Base> subclassesOf(serializer: KSerializer<T>) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After thinking about it for a while, I came to the idea that |
||
require(serializer is SealedClassSerializer) { | ||
"subClassesOf only supports automatic adding of subclasses of sealed types." | ||
} | ||
for ((subsubclass, subserializer) in serializer.class2Serializer.entries) { | ||
@Suppress("UNCHECKED_CAST") | ||
// We don't know the type here, but it matches if correct in the sealed serializer. | ||
subclass(subsubclass as KClass<T>, subserializer as KSerializer<T>) | ||
} | ||
} | ||
|
||
/** | ||
* Registers a [subclass] [serializer] in the resulting module under the [base class][Base]. | ||
*/ | ||
public fun <T : Base> subclass(subclass: KClass<T>, serializer: KSerializer<T>) { | ||
subclasses.add(subclass to serializer) | ||
subclasses[subclass] = serializer | ||
} | ||
|
||
/** | ||
|
@@ -116,3 +139,9 @@ public inline fun <Base : Any, reified T : Base> PolymorphicModuleBuilder<Base>. | |
*/ | ||
public inline fun <Base : Any, reified T : Base> PolymorphicModuleBuilder<Base>.subclass(clazz: KClass<T>): Unit = | ||
subclass(clazz, serializer()) | ||
|
||
/** | ||
* Registers the child serializers for the sealed class [T] in the resulting module under the [base class][Base]. | ||
*/ | ||
public inline fun <Base : Any, reified T : Base> PolymorphicModuleBuilder<Base>.subclassesOf(clazz: KClass<T>): Unit = | ||
subclassesOf(clazz.serializer()) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright 2017-2019 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.json.Json | ||
import kotlinx.serialization.modules.* | ||
import kotlinx.serialization.test.assertStringFormAndRestored | ||
import kotlin.test.Test | ||
|
||
class PolymorphicSealedChildTest { | ||
|
||
@Serializable | ||
data class FooHolder( | ||
val someMetadata: Int, | ||
val payload: List<@Polymorphic FooBase> | ||
) | ||
|
||
@Serializable | ||
abstract class FooBase | ||
|
||
@Serializable | ||
@SerialName("Foo") | ||
sealed class Foo: FooBase() { | ||
@Serializable | ||
@SerialName("Bar") | ||
data class Bar(val bar: Int) : Foo() | ||
@Serializable | ||
@SerialName("Baz") | ||
data class Baz(val baz: String) : Foo() | ||
} | ||
|
||
val sealedModule = SerializersModule { | ||
polymorphic(FooBase::class) { | ||
subclassesOf<Foo>() | ||
} | ||
} | ||
|
||
val json = Json { serializersModule = sealedModule } | ||
|
||
@Test | ||
fun testSaveSealedClassesList() { | ||
assertStringFormAndRestored( | ||
"""{"someMetadata":42,"payload":[ | ||
|{"type":"Bar","bar":1}, | ||
|{"type":"Baz","baz":"2"}]}""".trimMargin().replace("\n", ""), | ||
FooHolder(42, listOf(Foo.Bar(1), Foo.Baz("2"))), | ||
FooHolder.serializer(), | ||
json, | ||
printResult = true | ||
) | ||
} | ||
|
||
@Test | ||
fun testCanSerializeSealedClassPolymorphicallyOnTopLevel() { | ||
assertStringFormAndRestored( | ||
"""{"type":"Bar","bar":1}""", | ||
Foo.Bar(1), | ||
PolymorphicSerializer(FooBase::class), | ||
json | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation here does not mention that
Base
has to be sealed.Also, it would be more helpful if documentation contained the example in which scenario this function has to be used. I think #2199 has a good example with