Skip to content

Commit ba92e44

Browse files
committed
fixup! KTOR-8436 Support for config deserialization for map and merged configs
1 parent bdfd0d8 commit ba92e44

File tree

5 files changed

+38
-47
lines changed

5 files changed

+38
-47
lines changed

ktor-server/ktor-server-core/common/src/io/ktor/server/config/ApplicationConfig.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package io.ktor.server.config
77
import io.ktor.server.application.Application
88
import io.ktor.util.reflect.TypeInfo
99
import io.ktor.util.reflect.typeInfo
10-
import kotlin.reflect.typeOf
1110

1211
/**
1312
* Represents an application config node
@@ -157,12 +156,11 @@ public inline fun <reified E> Application.propertyOrNull(key: String): E? =
157156
* Converts the application config value to the given type parameter.
158157
*/
159158
public inline fun <reified E> ApplicationConfigValue.getAs(): E =
160-
if (E::class == String::class) {
161-
getString() as E
162-
} else if (typeOf<E>() == typeOf<List<String>>()) {
163-
getList() as E
164-
} else {
165-
getAs(typeInfo<E>()) as E
159+
when (val typeInfo = typeInfo<E>()) {
160+
typeInfo<String>() -> getString() as E
161+
typeInfo<List<String>>() -> getList() as E
162+
typeInfo<Map<String, Any?>>() -> getMap() as E
163+
else -> getAs(typeInfo) as E
166164
}
167165

168166
/**

ktor-server/ktor-server-core/common/src/io/ktor/server/config/MapConfigDecoder.kt

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ internal abstract class AbstractMapConfigDecoder(
2727
override fun decodeChar(): Char = decodeString().single()
2828
override fun decodeByte(): Byte = decodeString().toByte()
2929
override fun decodeShort(): Short = decodeString().toShort()
30+
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
31+
enumDescriptor.getElementIndex(decodeString())
32+
33+
protected fun beginStructure(descriptor: SerialDescriptor, path: String): CompositeDecoder {
34+
val kind = descriptor.kind as? StructureKind ?: error("Expected structure but found ${descriptor.kind}")
35+
return when (kind) {
36+
StructureKind.LIST -> ListMapConfigDecoder(map, path, map.listSize(path), serializersModule)
37+
StructureKind.MAP -> SubMapConfigDecoder(map, path, serializersModule)
38+
else -> MapConfigDecoder(map, path, serializersModule)
39+
}
40+
}
3041
}
3142

3243
@OptIn(ExperimentalSerializationApi::class)
@@ -59,17 +70,8 @@ internal class MapConfigDecoder(
5970
override fun decodeString(): String = map[currentPath]
6071
?: throw SerializationException("Property $currentPath not found")
6172

62-
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
63-
enumDescriptor.getElementIndex(decodeString())
64-
65-
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
66-
val kind = descriptor.kind as StructureKind
67-
return when (kind) {
68-
StructureKind.LIST -> ListMapConfigDecoder(map, currentPath, map.listSize(currentPath), serializersModule)
69-
StructureKind.MAP -> SubMapConfigDecoder(map, currentPath, serializersModule)
70-
else -> MapConfigDecoder(map, currentPath, serializersModule)
71-
}
72-
}
73+
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
74+
beginStructure(descriptor, currentPath)
7375
}
7476

7577
@OptIn(ExperimentalSerializationApi::class)
@@ -90,18 +92,8 @@ private class ListMapConfigDecoder(
9092
return map[key] ?: throw SerializationException("Missing list element at \"$key\"")
9193
}
9294

93-
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
94-
enumDescriptor.getElementIndex(decodeString())
95-
96-
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
97-
val currentKey = "$path.${index - 1}"
98-
val kind = descriptor.kind as StructureKind
99-
return when (kind) {
100-
StructureKind.LIST -> ListMapConfigDecoder(map, currentKey, map.listSize(currentKey), serializersModule)
101-
StructureKind.MAP -> SubMapConfigDecoder(map, currentKey, serializersModule)
102-
else -> MapConfigDecoder(map, currentKey, serializersModule)
103-
}
104-
}
95+
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
96+
beginStructure(descriptor, "$path.${index - 1}")
10597
}
10698

10799
@OptIn(ExperimentalSerializationApi::class)
@@ -141,15 +133,8 @@ private class SubMapConfigDecoder(
141133
}
142134
}
143135

144-
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
145-
val key = "$path.$currentKey"
146-
val kind = descriptor.kind as StructureKind
147-
return when (kind) {
148-
StructureKind.LIST -> ListMapConfigDecoder(map, key, map.listSize(key), serializersModule)
149-
StructureKind.MAP -> SubMapConfigDecoder(map, key, serializersModule)
150-
else -> MapConfigDecoder(map, key, serializersModule)
151-
}
152-
}
136+
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
137+
beginStructure(descriptor, "$path.$currentKey")
153138
}
154139

155140
private fun Map<String, String>.listSize(path: String): Int =

ktor-server/ktor-server-core/common/test/io/ktor/server/config/MapDecoderTest.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ class MapDecoderTest {
4646
@Serializable
4747
data class EnumConfig(
4848
val enum: TestEnum,
49-
val enumList: List<TestEnum>
49+
val enumList: List<TestEnum>,
50+
val enumMap: Map<String, TestEnum>
5051
)
5152

5253
@Test
@@ -162,14 +163,17 @@ class MapDecoderTest {
162163
"enumList.size" to "3",
163164
"enumList.0" to "ONE",
164165
"enumList.1" to "TWO",
165-
"enumList.2" to "THREE"
166+
"enumList.2" to "THREE",
167+
"enumMap.first" to "ONE",
168+
"enumMap.second" to "TWO",
166169
)
167170

168171
val decoder = MapConfigDecoder(map)
169172
val config = EnumConfig.serializer().deserialize(decoder)
170173

171174
assertEquals(TestEnum.TWO, config.enum)
172175
assertEquals(listOf(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE), config.enumList)
176+
assertEquals(mapOf("first" to TestEnum.ONE, "second" to TestEnum.TWO), config.enumMap)
173177
}
174178

175179
@Test

ktor-server/ktor-server-core/common/test/io/ktor/server/config/MergedApplicationConfigTest.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,15 @@ class MergedApplicationConfigTest {
109109
"nested.nested-value-2" to "value2",
110110
"nested.nested-value-3" to "value2",
111111
)
112-
val merged = first.mergeWith(second).toMap()["nested"] as Map<*, *>
113-
assertEquals("value1", merged["nested-value-1"])
114-
assertEquals("value2", merged["nested-value-2"])
115-
assertEquals("value2", merged["nested-value-3"])
112+
val merged = first.mergeWith(second)
113+
val nested = merged.toMap()["nested"] as Map<*, *>
114+
assertEquals("value1", nested["nested-value-1"])
115+
assertEquals("value2", nested["nested-value-2"])
116+
assertEquals("value2", nested["nested-value-3"])
117+
assertEquals(
118+
merged.config("nested").toMap(),
119+
merged.property("nested").getAs()
120+
)
116121
}
117122

118123
@Test

ktor-server/ktor-server-core/jvm/test/io/ktor/tests/config/HoconConfigTest.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import io.ktor.server.config.*
99
import kotlinx.serialization.Serializable
1010
import kotlin.test.*
1111

12-
class
13-
HoconConfigTest {
12+
class HoconConfigTest {
1413

1514
@Test
1615
fun testKeysTopLevelHoconConfig() {

0 commit comments

Comments
 (0)