Skip to content

Commit

Permalink
Add support of decoding map in the root of HOCON config (#1106)
Browse files Browse the repository at this point in the history
Fixes #1046
  • Loading branch information
shanshin authored Oct 21, 2020
1 parent 317ff19 commit 703f8e7
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
1 change: 1 addition & 0 deletions formats/hocon/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ dependencies {

api 'com.typesafe:config:1.3.2'

testCompile "org.jetbrains.kotlin:kotlin-test"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ public sealed class Hocon(
when {
descriptor.kind.listLike -> ListConfigReader(conf.getList(currentTag))
descriptor.kind.objLike -> if (ind > -1) ConfigReader(conf.getConfig(currentTag)) else this
descriptor.kind == StructureKind.MAP -> MapConfigReader(conf.getObject(currentTag))
descriptor.kind == StructureKind.MAP ->
// if current tag is null - map in the root of config
MapConfigReader(if (currentTagOrNull != null) conf.getObject(currentTag) else conf.root())
else -> this
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.serialization.hocon

import com.typesafe.config.ConfigFactory
import kotlinx.serialization.Serializable
import org.junit.Ignore
import org.junit.Test
import kotlin.test.*

class HoconRootMapTest {
private val configForRootMap = """
key1 {
a = "text1"
b = 11
}
key2 {
a = "text2"
b = 12
}
key3 {
a = "text3"
b = 13
}
"""
private val configWithEmptyObject = "{}"
private val configWithRootList = "[foo, bar]"
private val emptyConfig = ""

@Serializable
data class CompositeValue(
val a: String,
val b: Int
)

@Test
fun testConfigWithRootMap() {
val config = ConfigFactory.parseString(configForRootMap)
val obj = Hocon.decodeFromConfig<Map<String, CompositeValue>>(config)

assertEquals(CompositeValue("text1", 11), obj["key1"])
assertEquals(CompositeValue("text2", 12), obj["key2"])
assertEquals(CompositeValue("text3", 13), obj["key3"])
}

@Test
fun testEmptyObjectDecode() {
val config = ConfigFactory.parseString(configWithEmptyObject)
// non-null map decoded from empty object as empty map
val map = Hocon.decodeFromConfig<Map<String, String>>(config)
assertTrue(map.isEmpty())

// nullable map decoded from empty object as null - not obvious
assertNull(Hocon.decodeFromConfig<Map<String, String>?>(config))

// root-level list in config not supported but nullable list can be decoded from empty object
assertNull(Hocon.decodeFromConfig<List<String>?>(config))
}

@Ignore
@Test
fun testErrors() {
// because com.typesafe:config lib not support list in root we can't decode non-null list
val config = ConfigFactory.parseString(configWithEmptyObject)
assertFailsWith<NoSuchElementException> {
Hocon.decodeFromConfig<List<String>>(config)
}

// because com.typesafe:config lib not support list in root it fails while parsing
assertFailsWith<Exception> {
ConfigFactory.parseString(configWithRootList)
}

// com.typesafe:config lib parse empty config as empty object
ConfigFactory.parseString(emptyConfig)
}
}

0 comments on commit 703f8e7

Please sign in to comment.