From 80d289f8527f79ab5be306494ec5c42698b318c2 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 4 Mar 2023 22:05:16 +0900 Subject: [PATCH 1/4] Modified the name of isGetter to be changed by findImplicitPropertyName Because problems like #340 occur when using findRenameByField --- .../KotlinNamesAnnotationIntrospector.kt | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) 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 565487f6..312f10de 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt @@ -29,34 +29,34 @@ 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? = when (member) { - is AnnotatedMethod -> if (member.name.contains('-') && member.parameterCount == 0) { - when { - member.name.startsWith("get") -> member.name.substringAfter("get") - member.name.startsWith("is") -> member.name.substringAfter("is") - else -> null - }?.replaceFirstChar { it.lowercase(Locale.getDefault()) }?.substringBefore('-') - } else null - is AnnotatedParameter -> findKotlinParameterName(member) - else -> null - } - - // 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 - // (see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html and - // https://github.com/FasterXML/jackson-databind/issues/2527 - // for details) - override fun findRenameByField(config: MapperConfig<*>, - field: AnnotatedField, - implName: PropertyName): PropertyName? { - val origSimple = implName.simpleName - if (field.declaringClass.isKotlinClass() && origSimple.startsWith("is")) { - val mangledName: String? = BeanUtil.stdManglePropertyName(origSimple, 2) - if ((mangledName != null) && !mangledName.equals(origSimple)) { - return PropertyName.construct(mangledName) - } + override fun findImplicitPropertyName(member: AnnotatedMember): String? { + if (!member.declaringClass.isKotlinClass()) return null + + val name = member.name + + return when (member) { + is AnnotatedMethod -> if (member.parameterCount == 0) { + // The reason for truncating after `-` is to truncate the random suffix + // given after the value class accessor name. + when { + name.startsWith("get") -> name.takeIf { it.contains("-") }?.let { _ -> + name.substringAfter("get") + .replaceFirstChar { it.lowercase(Locale.getDefault()) } + .substringBefore('-') + } + // since 2.15: 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 + // (see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html and + // https://github.com/FasterXML/jackson-databind/issues/2527 and + // https://github.com/FasterXML/jackson-module-kotlin/issues/340 + // for details) + name.startsWith("is") -> if (name.contains("-")) name.substringAfter("-") else name + else -> null + } + } else null + is AnnotatedParameter -> findKotlinParameterName(member) + else -> null } - return null } @Suppress("UNCHECKED_CAST") From e99ea393e931f12a781ac7cf66d71909359a61c0 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 4 Mar 2023 22:07:47 +0900 Subject: [PATCH 2/4] Modify fixed test case and move package --- .../kotlin/test/github/{failing => }/Github340.kt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) rename src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/{failing => }/Github340.kt (63%) diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/failing/Github340.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt similarity index 63% rename from src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/failing/Github340.kt rename to src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt index dba6a074..d3ef57ec 100644 --- a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/failing/Github340.kt +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt @@ -1,10 +1,8 @@ -package com.fasterxml.jackson.module.kotlin.test.github.failing +package com.fasterxml.jackson.module.kotlin.test.github import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException import com.fasterxml.jackson.module.kotlin.kotlinModule import com.fasterxml.jackson.module.kotlin.readValue -import com.fasterxml.jackson.module.kotlin.test.expectFailure import org.junit.Test import kotlin.test.assertEquals @@ -21,10 +19,9 @@ class OwnerRequestTest { @Test fun testDeserHit340() { - expectFailure("GitHub #340 has been fixed!") { - val value: IsField = jackson.readValue(json) - assertEquals("Got a foo", value.foo) - } + val value: IsField = jackson.readValue(json) + // Fixed + assertEquals("Got a foo", value.foo) } @Test From 24e2968a75171f1f2253ccee2b6b66fc58412e10 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 4 Mar 2023 22:08:49 +0900 Subject: [PATCH 3/4] Added test case for when PropertyNamingStrategy is specified --- .../module/kotlin/test/ParameterNameTests.kt | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ParameterNameTests.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ParameterNameTests.kt index b3dd29a5..127b57bd 100644 --- a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ParameterNameTests.kt +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ParameterNameTests.kt @@ -25,24 +25,27 @@ class TestJacksonWithKotlin { val primaryAddress: String val wrongName: Boolean val createdDt: Date + val isName: Boolean fun validate( nameField: String = name, ageField: Int = age, addressField: String = primaryAddress, wrongNameField: Boolean = wrongName, - createDtField: Date = createdDt + createDtField: Date = createdDt, + isNameField: Boolean = isName, ) { assertThat(nameField, equalTo("Frank")) assertThat(ageField, equalTo(30)) assertThat(addressField, equalTo("something here")) assertThat(wrongNameField, equalTo(true)) assertThat(createDtField, equalTo(Date(1477419948000))) + assertThat(isNameField, equalTo(false)) } } - private val normalCasedJson = """{"name":"Frank","age":30,"primaryAddress":"something here","renamed":true,"createdDt":"2016-10-25T18:25:48.000+00:00"}""" - private val pascalCasedJson = """{"Name":"Frank","Age":30,"PrimaryAddress":"something here","Renamed":true,"CreatedDt":"2016-10-25T18:25:48.000+00:00"}""" + private val normalCasedJson = """{"name":"Frank","age":30,"primaryAddress":"something here","renamed":true,"createdDt":"2016-10-25T18:25:48.000+00:00","isName":false}""" + private val pascalCasedJson = """{"Name":"Frank","Age":30,"PrimaryAddress":"something here","Renamed":true,"CreatedDt":"2016-10-25T18:25:48.000+00:00","IsName":false}""" private val normalCasedMapper = jacksonObjectMapper() .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) @@ -65,6 +68,7 @@ class TestJacksonWithKotlin { override var primaryAddress: String = "" override var createdDt: Date = Date() + override val isName: Boolean = false } @Test fun NoFailWithDefaultAndSpecificConstructor() { @@ -79,7 +83,8 @@ class TestJacksonWithKotlin { override val age: Int, override val primaryAddress: String, val renamed: Boolean, - override val createdDt: Date + override val createdDt: Date, + override val isName: Boolean ) : TestFields { @JsonIgnore override val wrongName = renamed // here for the test validation only @@ -97,7 +102,8 @@ class TestJacksonWithKotlin { override val age: Int, override val primaryAddress: String, val renamed: Boolean, - override val createdDt: Date + override val createdDt: Date, + override val isName: Boolean ) : TestFields { @JsonIgnore override val wrongName = renamed // here for the test validation only @@ -121,7 +127,8 @@ class TestJacksonWithKotlin { override val age: Int, override val primaryAddress: String, @JsonProperty("renamed") override val wrongName: Boolean, - override val createdDt: Date + override val createdDt: Date, + override val isName: Boolean ) : TestFields @Test fun testDataClassWithExplicitJsonCreatorAndJsonProperty() { @@ -141,7 +148,8 @@ class TestJacksonWithKotlin { override val age: Int, override val primaryAddress: String, @JsonProperty("renamed") override val wrongName: Boolean, - override val createdDt: Date + override val createdDt: Date, + override val isName: Boolean ) : TestFields @Test fun testNormalClassWithJsonCreator() { @@ -155,7 +163,8 @@ class TestJacksonWithKotlin { private class StateObjectWithPartialFieldsInConstructor( override val name: String, override val age: Int, - override val primaryAddress: String + override val primaryAddress: String, + override val isName: Boolean ) : TestFields { @JsonProperty("renamed") override var wrongName: Boolean = false override var createdDt: Date by Delegates.notNull() @@ -176,7 +185,8 @@ class TestJacksonWithKotlin { override val age: Int, override val primaryAddress: String, @JsonProperty("renamed") override val wrongName: Boolean, - override val createdDt: Date + override val createdDt: Date, + override val isName: Boolean ) : TestFields @Test fun testDataClassWithNonFieldParametersInConstructor() { @@ -207,7 +217,8 @@ class TestJacksonWithKotlin { override val age: Int, override val primaryAddress: String, override val wrongName: Boolean, - override val createdDt: Date + override val createdDt: Date, + override val isName: Boolean ) : TestFields { var factoryUsed: Boolean = false companion object { @@ -216,9 +227,10 @@ class TestJacksonWithKotlin { @JsonProperty("age") age: Int, @JsonProperty("primaryAddress") primaryAddress: String, @JsonProperty("renamed") wrongName: Boolean, - @JsonProperty("createdDt") createdDt: Date + @JsonProperty("createdDt") createdDt: Date, + @JsonProperty("isName") isName: Boolean ): StateObjectWithFactory { - val obj = StateObjectWithFactory(nameThing, age, primaryAddress, wrongName, createdDt) + val obj = StateObjectWithFactory(nameThing, age, primaryAddress, wrongName, createdDt, isName) obj.factoryUsed = true return obj } @@ -236,7 +248,8 @@ class TestJacksonWithKotlin { val age: Int, val primaryAddress: String, val renamed: Boolean, - val createdDt: Date + val createdDt: Date, + val isName: Boolean ) { companion object { @JvmStatic @JsonCreator fun create( @@ -244,9 +257,10 @@ class TestJacksonWithKotlin { age: Int, primaryAddress: String, renamed: Boolean, - createdDt: Date + createdDt: Date, + isName: Boolean ): StateObjectWithFactoryNoParamAnnotations { - return StateObjectWithFactoryNoParamAnnotations(name, age, primaryAddress, renamed, createdDt) + return StateObjectWithFactoryNoParamAnnotations(name, age, primaryAddress, renamed, createdDt, isName) } } } @@ -266,7 +280,8 @@ class TestJacksonWithKotlin { override val age: Int, override val primaryAddress: String, override val wrongName: Boolean, - override val createdDt: Date + override val createdDt: Date, + override val isName: Boolean ) : TestFields { var factoryUsed: Boolean = false private companion object Named { @@ -275,9 +290,10 @@ class TestJacksonWithKotlin { @JsonProperty("age") age: Int, @JsonProperty("primaryAddress") primaryAddress: String, @JsonProperty("renamed") wrongName: Boolean, - @JsonProperty("createdDt") createdDt: Date + @JsonProperty("createdDt") createdDt: Date, + @JsonProperty("isName") isName: Boolean ): StateObjectWithFactoryOnNamedCompanion { - val obj = StateObjectWithFactoryOnNamedCompanion(nameThing, age, primaryAddress, wrongName, createdDt) + val obj = StateObjectWithFactoryOnNamedCompanion(nameThing, age, primaryAddress, wrongName, createdDt, isName) obj.factoryUsed = true return obj } From 8ada33f383c831c8a77fb8652494e67cf14c2f4e Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 4 Mar 2023 22:34:23 +0900 Subject: [PATCH 4/4] Added test case for isSetter to work. --- .../jackson/module/kotlin/test/github/Github340.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt index d3ef57ec..7d38cbd3 100644 --- a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt @@ -1,6 +1,7 @@ package com.fasterxml.jackson.module.kotlin.test.github import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.kotlinModule import com.fasterxml.jackson.module.kotlin.readValue import org.junit.Test @@ -29,4 +30,17 @@ class OwnerRequestTest { val value: NoIsField = jackson.readValue(json) assertEquals("Got a foo", value.foo) } + + // A test case for isSetter to work, added with the fix for this issue. + class IsSetter { + lateinit var isFoo: String + } + + @Test + fun isSetterTest() { + val json = """{"isFoo":"bar"}""" + val isSetter: IsSetter = jackson.readValue(json) + + assertEquals("bar", isSetter.isFoo) + } }