Skip to content

Commit 6f9d62f

Browse files
committed
KTOR-8654 Fix optionals in config deserialization in HOCON/Map
1 parent 3c190b9 commit 6f9d62f

File tree

4 files changed

+55
-0
lines changed

4 files changed

+55
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ internal class MapConfigDecoder(
5959
return if (map.containsPrefix(fullPath)) {
6060
currentPath = fullPath
6161
newIndex
62+
} else if (descriptor.isElementOptional(newIndex)) {
63+
decodeElementIndex(descriptor)
6264
} else {
6365
CompositeDecoder.DECODE_DONE
6466
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ class MapDecoderTest {
5050
val enumMap: Map<String, TestEnum>
5151
)
5252

53+
@Serializable
54+
data class Address(
55+
val streetName: String,
56+
val postalCode: String,
57+
val unitNumber: String? = null,
58+
val municipality: String,
59+
)
60+
5361
@Test
5462
fun testSimpleTypes() {
5563
val map = mapOf(
@@ -206,4 +214,19 @@ class MapDecoderTest {
206214
SimpleConfig.serializer().deserialize(decoder)
207215
}
208216
}
217+
218+
@Test
219+
fun testOptionals() {
220+
val map = mapOf(
221+
"streetName" to "Test street",
222+
"postalCode" to "12345",
223+
"municipality" to "Test municipality"
224+
)
225+
val decoder = MapConfigDecoder(map)
226+
val config = Address.serializer().deserialize(decoder)
227+
assertEquals("Test street", config.streetName)
228+
assertEquals("12345", config.postalCode)
229+
assertEquals("Test municipality", config.municipality)
230+
assertEquals(null, config.unitNumber)
231+
}
209232
}

ktor-server/ktor-server-core/jvm/src/io/ktor/server/config/HoconDecoder.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ internal open class HoconDecoder(
3030
return if (config.hasPath(fullPath)) {
3131
currentPath = fullPath
3232
newIndex
33+
} else if (descriptor.isElementOptional(newIndex)) {
34+
decodeElementIndex(descriptor)
3335
} else {
3436
CompositeDecoder.DECODE_DONE
3537
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,26 @@ class HoconDecoderTest {
220220
}
221221
}
222222

223+
@Test
224+
fun `default values`() {
225+
val content = """
226+
address {
227+
streetName = "Main Street"
228+
postalCode = "12345"
229+
municipality = "Test City"
230+
# unitNumber is intentionally omitted to test default value
231+
}
232+
""".trimIndent()
233+
234+
val address = parseConfig(content)
235+
.propertyOrNull("address")?.getAs<Address>()
236+
237+
assertNotNull(address)
238+
assertEquals("Main Street", address.streetName)
239+
assertEquals("Test City", address.municipality)
240+
assertNull(address.unitNumber)
241+
}
242+
223243
private fun parseConfig(content: String): HoconApplicationConfig =
224244
HoconApplicationConfig(ConfigFactory.parseString(content))
225245

@@ -294,4 +314,12 @@ class HoconDecoderTest {
294314
val enumValue: TestEnum,
295315
val enumList: List<TestEnum>
296316
)
317+
318+
@Serializable
319+
data class Address(
320+
val streetName: String,
321+
val postalCode: String,
322+
val unitNumber: String? = null,
323+
val municipality: String,
324+
)
297325
}

0 commit comments

Comments
 (0)