@@ -25,8 +25,10 @@ import kotlinx.serialization.modules.*
2525 */
2626@ExperimentalSerializationApi
2727public sealed class Hocon (
28- internal val useConfigNamingConvention : Boolean ,
29- override val serializersModule : SerializersModule
28+ internal val useConfigNamingConvention : Boolean ,
29+ internal val useArrayPolymorphism : Boolean ,
30+ internal val classDiscriminator : String ,
31+ override val serializersModule : SerializersModule
3032) : SerialFormat {
3133
3234 @ExperimentalSerializationApi
@@ -37,7 +39,7 @@ public sealed class Hocon(
3739 * The default instance of Hocon parser.
3840 */
3941 @ExperimentalSerializationApi
40- public companion object Default : Hocon(false , EmptySerializersModule ) {
42+ public companion object Default : Hocon(false , false , " type " , EmptySerializersModule ) {
4143 private val NAMING_CONVENTION_REGEX by lazy { " [A-Z]" .toRegex() }
4244 }
4345
@@ -119,15 +121,44 @@ public sealed class Hocon(
119121 return decodeTaggedNotNullMark(currentTag)
120122 }
121123
122- override fun beginStructure (descriptor : SerialDescriptor ): CompositeDecoder =
123- when {
124- descriptor.kind.listLike -> ListConfigReader (conf.getList(currentTag))
125- descriptor.kind.objLike -> if (ind > - 1 ) ConfigReader (conf.getConfig(currentTag)) else this
126- descriptor.kind == StructureKind .MAP ->
124+ override fun <T > decodeSerializableValue (deserializer : DeserializationStrategy <T >): T {
125+ if (deserializer !is AbstractPolymorphicSerializer <* > || useArrayPolymorphism) {
126+ return deserializer.deserialize(this )
127+ }
128+
129+ val config = if (currentTagOrNull != null ) conf.getConfig(currentTag) else conf
130+
131+ val reader = ConfigReader (config)
132+ val type = reader.decodeTaggedString(classDiscriminator)
133+ val actualSerializer = deserializer.findPolymorphicSerializerOrNull(reader, type)
134+ ? : throwSerializerNotFound(type)
135+
136+ @Suppress(" UNCHECKED_CAST" )
137+ return (actualSerializer as DeserializationStrategy <T >).deserialize(reader)
138+ }
139+
140+ private fun throwSerializerNotFound (type : String? ): Nothing {
141+ val suffix = if (type == null ) " missing class discriminator ('null')" else " class discriminator '$type '"
142+ throw SerializationException (" Polymorphic serializer was not found for $suffix " )
143+ }
144+
145+ override fun beginStructure (descriptor : SerialDescriptor ): CompositeDecoder {
146+ val kind = when (descriptor.kind) {
147+ is PolymorphicKind -> {
148+ if (useArrayPolymorphism) StructureKind .LIST else StructureKind .MAP
149+ }
150+ else -> descriptor.kind
151+ }
152+
153+ return when {
154+ kind.listLike -> ListConfigReader (conf.getList(currentTag))
155+ kind.objLike -> if (ind > - 1 ) ConfigReader (conf.getConfig(currentTag)) else this
156+ kind == StructureKind .MAP ->
127157 // if current tag is null - map in the root of config
128158 MapConfigReader (if (currentTagOrNull != null ) conf.getObject(currentTag) else conf.root())
129159 else -> this
130160 }
161+ }
131162 }
132163
133164 private inner class ListConfigReader (private val list : ConfigList ) : ConfigConverter<Int>() {
@@ -216,7 +247,7 @@ public inline fun <reified T> Hocon.decodeFromConfig(config: Config): T =
216247public fun Hocon (from : Hocon = Hocon , builderAction : HoconBuilder .() -> Unit ): Hocon {
217248 val builder = HoconBuilder (from)
218249 builder.builderAction()
219- return HoconImpl (builder.useConfigNamingConvention, builder.serializersModule)
250+ return HoconImpl (builder.useConfigNamingConvention, builder.useArrayPolymorphism, builder.classDiscriminator, builder. serializersModule)
220251}
221252
222253/* *
@@ -233,10 +264,25 @@ public class HoconBuilder internal constructor(hocon: Hocon) {
233264 * Switches naming resolution to config naming convention: hyphen separated.
234265 */
235266 public var useConfigNamingConvention: Boolean = hocon.useConfigNamingConvention
267+
268+ /* *
269+ * Switches polymorphic serialization to the default array format.
270+ * This is an option for legacy polymorphism format and should not be generally used.
271+ * `false` by default.
272+ */
273+ public var useArrayPolymorphism: Boolean = hocon.useArrayPolymorphism
274+
275+ /* *
276+ * Name of the class descriptor property for polymorphic serialization.
277+ * "type" by default.
278+ */
279+ public var classDiscriminator: String = hocon.classDiscriminator
236280}
237281
238282@OptIn(ExperimentalSerializationApi ::class )
239283private class HoconImpl (
240- useConfigNamingConvention : Boolean = false ,
241- serializersModule : SerializersModule = EmptySerializersModule
242- ): Hocon(useConfigNamingConvention, serializersModule)
284+ useConfigNamingConvention : Boolean ,
285+ useArrayPolymorphism : Boolean ,
286+ classDiscriminator : String ,
287+ serializersModule : SerializersModule
288+ ) : Hocon(useConfigNamingConvention, useArrayPolymorphism, classDiscriminator, serializersModule)
0 commit comments