diff --git a/pom.xml b/pom.xml
index 5a1635020..b63cc7501 100644
--- a/pom.xml
+++ b/pom.xml
@@ -221,6 +221,11 @@
com.fasterxml.jackson.module.kotlin.ULongSerializer
com.fasterxml.jackson.module.kotlin.UShortDeserializer
com.fasterxml.jackson.module.kotlin.UShortSerializer
+
+ com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospectorKt
diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt
index e71d5e099..93d09f846 100644
--- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt
+++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt
@@ -18,37 +18,41 @@ import java.util.Locale
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
-import kotlin.reflect.full.companionObject
-import kotlin.reflect.full.declaredFunctions
-import kotlin.reflect.full.memberProperties
-import kotlin.reflect.full.primaryConstructor
+import kotlin.reflect.full.*
import kotlin.reflect.jvm.internal.KotlinReflectionInternalError
+import kotlin.reflect.jvm.javaGetter
import kotlin.reflect.jvm.javaType
import kotlin.reflect.jvm.kotlinFunction
internal class KotlinNamesAnnotationIntrospector(val module: KotlinModule, val cache: ReflectionCache, val ignoredClassesForImplyingJsonCreator: Set>) : NopAnnotationIntrospector() {
// since 2.4
- override fun findImplicitPropertyName(member: AnnotatedMember): String? {
- if (member is AnnotatedMethod && member.isInlineClass()) {
- if (member.name.startsWith("get") &&
- member.name.contains('-') &&
- member.parameterCount == 0) {
- return member.name.substringAfter("get")
- .replaceFirstChar { it.lowercase(Locale.getDefault()) }
- .substringBefore('-')
- } else if (member.name.startsWith("is") &&
- member.name.contains('-') &&
- member.parameterCount == 0) {
- return member.name.substringAfter("is")
- .replaceFirstChar { it.lowercase(Locale.getDefault()) }
- .substringBefore('-')
- }
- } else if (member is AnnotatedParameter) {
- return findKotlinParameterName(member)
+ override fun findImplicitPropertyName(member: AnnotatedMember): String? = when (member) {
+ is AnnotatedMethod -> findImplicitPropertyNameFromKotlinPropertyIfNeeded(member)
+ is AnnotatedParameter -> findKotlinParameterName(member)
+ else -> null
+ }
+
+ // Use Kotlin property names as needed.
+ private fun findImplicitPropertyNameFromKotlinPropertyIfNeeded(member: AnnotatedMethod): String? = member
+ .takeIf {
+ it.parameterCount == 0 && looksLikeKotlinGeneratedMethod(it.name) && !it.hasAnnotation(JvmName::class.java)
+ }?.let { _ ->
+ member.member.kotlinFunction
+ ?.let { getterLike ->
+ // for getter like method
+ val name = getterLike.name
+ val replaceTarget = if (name.startsWith("get")) "get" else "is"
+
+ name.substringAfter(replaceTarget).replaceFirstChar { it.lowercase(Locale.getDefault()) }
+ }
+ ?: member.declaringClass.kotlin.declaredMemberProperties // for property getter
+ .find { kProperty -> kProperty.javaGetter == member.member }
+ ?.name
}
- return null
- }
+ // Since getter for value class (inline class) will be compiled into a different name such as "getFoo-${random}".
+ private fun looksLikeKotlinGeneratedMethod(methodName: String) =
+ methodName.run { contains("-") && (startsWith("get") || startsWith("is")) }
// since 2.11: support Kotlin's way of handling "isXxx" backed properties where
// logical property name needs to remain "isXxx" and not become "xxx" as with Java Beans
@@ -169,5 +173,3 @@ internal class KotlinNamesAnnotationIntrospector(val module: KotlinModule, val c
}
}
}
-
-private fun AnnotatedMethod.isInlineClass() = declaringClass.declaredMethods.any { it.name.contains('-') }
diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github356.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github356.kt
index d77072e89..821081825 100644
--- a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github356.kt
+++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github356.kt
@@ -21,6 +21,30 @@ class TestGithub356 {
assertEquals("""{"inlineClassProperty":"bar"}""", mapper.writeValueAsString(original))
}
+ @Test
+ fun deserializeKebabInlineMember() {
+ val original = ClassWithKebabInlineMember(ValueClass("bar"))
+ assertEquals(original, mapper.readValue(mapper.writeValueAsString(original)))
+ }
+
+ @Test
+ fun serializeKebabInlineClass() {
+ val original = ClassWithKebabInlineMember(ValueClass("bar"))
+ assertEquals("""{"value-class-property":"bar"}""", mapper.writeValueAsString(original))
+ }
+
+ @Test
+ fun deserializeNamedInlineClass() {
+ val original = ClassWithNamedInlineMember(ValueClass("bar"))
+ assertEquals(original, mapper.readValue(mapper.writeValueAsString(original)))
+ }
+
+ @Test
+ fun serializeNamedInlineClass() {
+ val original = ClassWithNamedInlineMember(ValueClass("bar"))
+ assertEquals("""{"value-":"bar"}""", mapper.writeValueAsString(original))
+ }
+
@Test
fun deserializeValueClass() {
val original = ClassWithValueMember(ValueClass("bar"))
@@ -54,3 +78,17 @@ data class ClassWithValueMember(val valueClassProperty: ValueClass) {
fun build() = ClassWithValueMember(ValueClass(valueClassProperty))
}
}
+
+@JsonDeserialize(builder = ClassWithKebabInlineMember.JacksonBuilder::class)
+data class ClassWithKebabInlineMember(val `value-class-property`: ValueClass) {
+ data class JacksonBuilder constructor(val `value-class-property`: String) {
+ fun build() = ClassWithKebabInlineMember(ValueClass(`value-class-property`))
+ }
+}
+
+@JsonDeserialize(builder = ClassWithNamedInlineMember.JacksonBuilder::class)
+data class ClassWithNamedInlineMember(@get:JvmName("getValue-") val `value-`: ValueClass) {
+ data class JacksonBuilder constructor(val `value-`: String) {
+ fun build() = ClassWithNamedInlineMember(ValueClass(`value-`))
+ }
+}