summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt <matt.mmor@gmail.com>2023-12-05 18:19:18 +0000
committerGitHub <noreply@github.com>2023-12-05 19:19:18 +0100
commitd8b98b5ed3ff5f8c6074a1b59e6f0e23f62f0dea (patch)
tree5fc889ef4beb62db349dfd4a4d2ca557f227ada7
parentb9945728413253cd5d05141b5e1a26f1130da777 (diff)
downloadkotlinx.serialization-d8b98b5ed3ff5f8c6074a1b59e6f0e23f62f0dea.tar.gz
Fix: Hocon polymorphic serialization (#2151)
Fixes #1581
-rw-r--r--formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt31
-rw-r--r--formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt48
2 files changed, 76 insertions, 3 deletions
diff --git a/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt
index f2f27794..5ca445ec 100644
--- a/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt
+++ b/formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt
@@ -145,7 +145,7 @@ public sealed class Hocon(
}
- private inner class ConfigReader(val conf: Config) : ConfigConverter<String>() {
+ private inner class ConfigReader(val conf: Config, private val isPolymorphic: Boolean = false) : ConfigConverter<String>() {
private var ind = -1
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
@@ -161,8 +161,10 @@ public sealed class Hocon(
private fun composeName(parentName: String, childName: String) =
if (parentName.isEmpty()) childName else "$parentName.$childName"
- override fun SerialDescriptor.getTag(index: Int): String =
- composeName(currentTagOrNull.orEmpty(), getConventionElementName(index, useConfigNamingConvention))
+ override fun SerialDescriptor.getTag(index: Int): String {
+ val conventionName = getConventionElementName(index, useConfigNamingConvention)
+ return if (!isPolymorphic) composeName(currentTagOrNull.orEmpty(), conventionName) else conventionName
+ }
override fun decodeNotNullMark(): Boolean {
// Tag might be null for top-level deserialization
@@ -206,6 +208,27 @@ public sealed class Hocon(
}
}
+ private inner class PolymorphConfigReader(private val conf: Config) : ConfigConverter<String>() {
+ private var ind = -1
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+ when {
+ descriptor.kind.objLike -> ConfigReader(conf, isPolymorphic = true)
+ else -> this
+ }
+
+ override fun SerialDescriptor.getTag(index: Int): String = getElementName(index)
+
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+ ind++
+ return if (ind >= descriptor.elementsCount) DECODE_DONE else ind
+ }
+
+ override fun <E> getValueFromTaggedConfig(tag: String, valueResolver: (Config, String) -> E): E {
+ return valueResolver(conf, tag)
+ }
+ }
+
private inner class ListConfigReader(private val list: ConfigList) : ConfigConverter<Int>() {
private var ind = -1
@@ -216,6 +239,7 @@ public sealed class Hocon(
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
when {
+ descriptor.kind is PolymorphicKind -> PolymorphConfigReader((list[currentTag] as ConfigObject).toConfig())
descriptor.kind.listLike -> ListConfigReader(list[currentTag] as ConfigList)
descriptor.kind.objLike -> ConfigReader((list[currentTag] as ConfigObject).toConfig())
descriptor.kind == StructureKind.MAP -> MapConfigReader(list[currentTag] as ConfigObject)
@@ -256,6 +280,7 @@ public sealed class Hocon(
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
when {
+ descriptor.kind is PolymorphicKind -> PolymorphConfigReader((values[currentTag / 2] as ConfigObject).toConfig())
descriptor.kind.listLike -> ListConfigReader(values[currentTag / 2] as ConfigList)
descriptor.kind.objLike -> ConfigReader((values[currentTag / 2] as ConfigObject).toConfig())
descriptor.kind == StructureKind.MAP -> MapConfigReader(values[currentTag / 2] as ConfigObject)
diff --git a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt
index db038e70..1dbc1f90 100644
--- a/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt
+++ b/formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconPolymorphismTest.kt
@@ -24,6 +24,12 @@ class HoconPolymorphismTest {
}
@Serializable
+ data class SealedCollectionContainer(val sealed: Collection<Sealed>)
+
+ @Serializable
+ data class SealedMapContainer(val sealed: Map<String, Sealed>)
+
+ @Serializable
data class CompositeClass(var sealed: Sealed)
@@ -102,4 +108,46 @@ class HoconPolymorphismTest {
serializer = Sealed.serializer(),
)
}
+
+ @Test
+ fun testCollectionContainer() {
+ objectHocon.assertStringFormAndRestored(
+ expected = """
+ sealed = [
+ { type = annotated_type_child, my_type = override, intField = 3 }
+ { type = object }
+ { type = data_class, name = testDataClass, intField = 1 }
+ ]
+ """.trimIndent(),
+ original = SealedCollectionContainer(
+ listOf(
+ Sealed.AnnotatedTypeChild(type = "override"),
+ Sealed.ObjectChild,
+ Sealed.DataClassChild(name = "testDataClass"),
+ )
+ ),
+ serializer = SealedCollectionContainer.serializer(),
+ )
+ }
+
+ @Test
+ fun testMapContainer() {
+ objectHocon.assertStringFormAndRestored(
+ expected = """
+ sealed = {
+ "annotated_type_child" = { type = annotated_type_child, my_type = override, intField = 3 }
+ "object" = { type = object }
+ "data_class" = { type = data_class, name = testDataClass, intField = 1 }
+ }
+ """.trimIndent(),
+ original = SealedMapContainer(
+ mapOf(
+ "annotated_type_child" to Sealed.AnnotatedTypeChild(type = "override"),
+ "object" to Sealed.ObjectChild,
+ "data_class" to Sealed.DataClassChild(name = "testDataClass"),
+ )
+ ),
+ serializer = SealedMapContainer.serializer(),
+ )
+ }
}