@@ -25,24 +25,45 @@ import kotlinx.serialization.modules.*
2525 */
2626@ExperimentalSerializationApi
2727public sealed class Hocon (
28- internal val useConfigNamingConvention : Boolean ,
29- internal val useArrayPolymorphism : Boolean ,
30- internal val classDiscriminator : String ,
31- override val serializersModule : SerializersModule
28+ internal val encodeDefaults : Boolean ,
29+ internal val useConfigNamingConvention : Boolean ,
30+ internal val useArrayPolymorphism : Boolean ,
31+ internal val classDiscriminator : String ,
32+ override val serializersModule : SerializersModule ,
3233) : SerialFormat {
3334
35+ /* *
36+ * Decodes the given [config] into a value of type [T] using the given serializer.
37+ */
3438 @ExperimentalSerializationApi
3539 public fun <T > decodeFromConfig (deserializer : DeserializationStrategy <T >, config : Config ): T =
3640 ConfigReader (config).decodeSerializableValue(deserializer)
3741
3842 /* *
39- * The default instance of Hocon parser.
43+ * Encodes the given [value] into a [Config] using the given [serializer].
44+ * @throws SerializationException If list or primitive type passed as a [value].
4045 */
4146 @ExperimentalSerializationApi
42- public companion object Default : Hocon(false , false , " type" , EmptySerializersModule ) {
43- private val NAMING_CONVENTION_REGEX by lazy { " [A-Z]" .toRegex() }
47+ public fun <T > encodeToConfig (serializer : SerializationStrategy <T >, value : T ): Config {
48+ lateinit var configValue: ConfigValue
49+ val encoder = HoconConfigEncoder (this ) { configValue = it }
50+ encoder.encodeSerializableValue(serializer, value)
51+
52+ if (configValue !is ConfigObject ) {
53+ throw SerializationException (
54+ " Value of type '${configValue.valueType()} ' can't be used at the root of HOCON Config. " +
55+ " It should be either object or map."
56+ )
57+ }
58+ return (configValue as ConfigObject ).toConfig()
4459 }
4560
61+ /* *
62+ * The default instance of Hocon parser.
63+ */
64+ @ExperimentalSerializationApi
65+ public companion object Default : Hocon(false , false , false , " type" , EmptySerializersModule )
66+
4667 private abstract inner class ConfigConverter <T > : TaggedDecoder <T >() {
4768 override val serializersModule: SerializersModule
4869 get() = this @Hocon.serializersModule
@@ -59,8 +80,7 @@ public sealed class Hocon(
5980 }
6081 } catch (e: ConfigException ) {
6182 val configOrigin = e.origin()
62- val requiredType = E ::class .simpleName
63- throw SerializationException (" ${configOrigin.description()} required to be of type $requiredType " )
83+ throw ConfigValueTypeCastException <E >(configOrigin)
6484 }
6585 }
6686
@@ -109,13 +129,7 @@ public sealed class Hocon(
109129 if (parentName.isEmpty()) childName else " $parentName .$childName "
110130
111131 override fun SerialDescriptor.getTag (index : Int ): String =
112- composeName(currentTagOrNull ? : " " , getConventionElementName(index))
113-
114- private fun SerialDescriptor.getConventionElementName (index : Int ): String {
115- val originalName = getElementName(index)
116- return if (! useConfigNamingConvention) originalName
117- else originalName.replace(NAMING_CONVENTION_REGEX ) { " -${it.value.lowercase()} " }
118- }
132+ composeName(currentTagOrNull.orEmpty(), getConventionElementName(index, useConfigNamingConvention))
119133
120134 override fun decodeNotNullMark (): Boolean {
121135 // Tag might be null for top-level deserialization
@@ -133,24 +147,14 @@ public sealed class Hocon(
133147 val reader = ConfigReader (config)
134148 val type = reader.decodeTaggedString(classDiscriminator)
135149 val actualSerializer = deserializer.findPolymorphicSerializerOrNull(reader, type)
136- ? : throwSerializerNotFound (type)
150+ ? : throw SerializerNotFoundException (type)
137151
138152 @Suppress(" UNCHECKED_CAST" )
139153 return (actualSerializer as DeserializationStrategy <T >).deserialize(reader)
140154 }
141155
142- private fun throwSerializerNotFound (type : String? ): Nothing {
143- val suffix = if (type == null ) " missing class discriminator ('null')" else " class discriminator '$type '"
144- throw SerializationException (" Polymorphic serializer was not found for $suffix " )
145- }
146-
147156 override fun beginStructure (descriptor : SerialDescriptor ): CompositeDecoder {
148- val kind = when (descriptor.kind) {
149- is PolymorphicKind -> {
150- if (useArrayPolymorphism) StructureKind .LIST else StructureKind .MAP
151- }
152- else -> descriptor.kind
153- }
157+ val kind = descriptor.hoconKind(useArrayPolymorphism)
154158
155159 return when {
156160 kind.listLike -> ListConfigReader (conf.getList(currentTag))
@@ -239,28 +243,31 @@ public sealed class Hocon(
239243 throw SerializationException (" $serialName does not contain element with name '$name '" )
240244 return index
241245 }
242-
243- private val SerialKind .listLike get() = this == StructureKind .LIST || this is PolymorphicKind
244- private val SerialKind .objLike get() = this == StructureKind .CLASS || this == StructureKind .OBJECT
245246}
246247
247248/* *
248- * Decodes the given [config] into a value of type [T] using a deserialize retrieved
249- * from reified type parameter.
249+ * Decodes the given [config] into a value of type [T] using a deserializer retrieved
250+ * from the reified type parameter.
250251 */
251252@ExperimentalSerializationApi
252253public inline fun <reified T > Hocon.decodeFromConfig (config : Config ): T =
253254 decodeFromConfig(serializersModule.serializer(), config)
254255
256+ /* *
257+ * Encodes the given [value] of type [T] into a [Config] using a serializer retrieved
258+ * from the reified type parameter.
259+ */
260+ @ExperimentalSerializationApi
261+ public inline fun <reified T > Hocon.encodeToConfig (value : T ): Config =
262+ encodeToConfig(serializersModule.serializer(), value)
263+
255264/* *
256265 * Creates an instance of [Hocon] configured from the optionally given [Hocon instance][from]
257266 * and adjusted with [builderAction].
258267 */
259268@ExperimentalSerializationApi
260269public fun Hocon (from : Hocon = Hocon , builderAction : HoconBuilder .() -> Unit ): Hocon {
261- val builder = HoconBuilder (from)
262- builder.builderAction()
263- return HoconImpl (builder.useConfigNamingConvention, builder.useArrayPolymorphism, builder.classDiscriminator, builder.serializersModule)
270+ return HoconImpl (HoconBuilder (from).apply (builderAction))
264271}
265272
266273/* *
@@ -273,6 +280,12 @@ public class HoconBuilder internal constructor(hocon: Hocon) {
273280 */
274281 public var serializersModule: SerializersModule = hocon.serializersModule
275282
283+ /* *
284+ * Specifies whether default values of Kotlin properties should be encoded.
285+ * `false` by default.
286+ */
287+ public var encodeDefaults: Boolean = hocon.encodeDefaults
288+
276289 /* *
277290 * Switches naming resolution to config naming convention: hyphen separated.
278291 */
@@ -293,9 +306,10 @@ public class HoconBuilder internal constructor(hocon: Hocon) {
293306}
294307
295308@OptIn(ExperimentalSerializationApi ::class )
296- private class HoconImpl (
297- useConfigNamingConvention : Boolean ,
298- useArrayPolymorphism : Boolean ,
299- classDiscriminator : String ,
300- serializersModule : SerializersModule
301- ) : Hocon(useConfigNamingConvention, useArrayPolymorphism, classDiscriminator, serializersModule)
309+ private class HoconImpl (hoconBuilder : HoconBuilder ) : Hocon(
310+ encodeDefaults = hoconBuilder.encodeDefaults,
311+ useConfigNamingConvention = hoconBuilder.useConfigNamingConvention,
312+ useArrayPolymorphism = hoconBuilder.useArrayPolymorphism,
313+ classDiscriminator = hoconBuilder.classDiscriminator,
314+ serializersModule = hoconBuilder.serializersModule
315+ )
0 commit comments