Skip to content

Commit

Permalink
Fix conversion of input objects with Optional fields
Browse files Browse the repository at this point in the history
Update our converter to delegate to Spring's ObjectToOptionalConverter to support
conversion to target types with Optional fields / properties.
  • Loading branch information
kilink committed Jun 27, 2024
1 parent 830ae96 commit b1347d7
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.springframework.core.convert.support.DefaultConversionService
import org.springframework.util.CollectionUtils
import org.springframework.util.ReflectionUtils
import java.lang.reflect.Type
import java.util.Optional
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.primaryConstructor
Expand All @@ -54,6 +55,10 @@ class DefaultInputObjectMapper(customInputObjectMapper: InputObjectMapper? = nul
}

override fun matches(sourceType: TypeDescriptor, targetType: TypeDescriptor): Boolean {
if (targetType.type == Optional::class.java) {
// Let Spring's ObjectToOptionalConverter handle it
return false
}
if (sourceType.isMap) {
val keyDescriptor = sourceType.mapKeyTypeDescriptor
return keyDescriptor == null || keyDescriptor.type == String::class.java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test
import java.time.LocalDateTime
import java.util.Optional
import kotlin.reflect.KClass

internal class InputObjectMapperTest {
Expand Down Expand Up @@ -247,6 +248,17 @@ internal class InputObjectMapperTest {
assertThat(result.items).isEqualTo(setOf(1, 2, 3, 4))
}

@Test
fun `mapping to an object with Optional fields works`() {
var result = inputObjectMapper.mapToKotlinObject(mapOf<String, Any?>("foo" to null, "bar" to mapOf("subkey1" to "subkey1-value")), InputWithOptional::class)
assertThat(result.foo).isNotPresent
assertThat(result.bar).get().isEqualTo(KotlinSubObject("subkey1-value"))

result = inputObjectMapper.mapToKotlinObject(mapOf<String, Any?>("foo" to "foo-value", "bar" to null), InputWithOptional::class)
assertThat(result.foo).get().isEqualTo("foo-value")
assertThat(result.bar).isNotPresent
}

data class KotlinInputObject(val simpleString: String?, val someDate: LocalDateTime, val someObject: KotlinSomeObject)
data class KotlinNestedInputObject(val input: KotlinInputObject)
data class KotlinDoubleNestedInputObject(val inputL1: KotlinNestedInputObject)
Expand All @@ -259,4 +271,6 @@ internal class InputObjectMapperTest {

enum class FieldType { FOO, BAR, BAZ }
data class KotlinObjectWithEnumField(val name: String, val type: FieldType)

data class InputWithOptional(val foo: Optional<String>, val bar: Optional<KotlinSubObject>)
}

0 comments on commit b1347d7

Please sign in to comment.