Skip to content

Commit a3376b2

Browse files
authored
Merge pull request #641 from k163377/fix-#340
Fix #340
2 parents cf2b646 + 8ada33f commit a3376b2

File tree

3 files changed

+79
-52
lines changed

3 files changed

+79
-52
lines changed

src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,34 @@ import kotlin.reflect.jvm.kotlinFunction
2929

3030
internal class KotlinNamesAnnotationIntrospector(val module: KotlinModule, val cache: ReflectionCache, val ignoredClassesForImplyingJsonCreator: Set<KClass<*>>) : NopAnnotationIntrospector() {
3131
// since 2.4
32-
override fun findImplicitPropertyName(member: AnnotatedMember): String? = when (member) {
33-
is AnnotatedMethod -> if (member.name.contains('-') && member.parameterCount == 0) {
34-
when {
35-
member.name.startsWith("get") -> member.name.substringAfter("get")
36-
member.name.startsWith("is") -> member.name.substringAfter("is")
37-
else -> null
38-
}?.replaceFirstChar { it.lowercase(Locale.getDefault()) }?.substringBefore('-')
39-
} else null
40-
is AnnotatedParameter -> findKotlinParameterName(member)
41-
else -> null
42-
}
43-
44-
// since 2.11: support Kotlin's way of handling "isXxx" backed properties where
45-
// logical property name needs to remain "isXxx" and not become "xxx" as with Java Beans
46-
// (see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html and
47-
// https://github.com/FasterXML/jackson-databind/issues/2527
48-
// for details)
49-
override fun findRenameByField(config: MapperConfig<*>,
50-
field: AnnotatedField,
51-
implName: PropertyName): PropertyName? {
52-
val origSimple = implName.simpleName
53-
if (field.declaringClass.isKotlinClass() && origSimple.startsWith("is")) {
54-
val mangledName: String? = BeanUtil.stdManglePropertyName(origSimple, 2)
55-
if ((mangledName != null) && !mangledName.equals(origSimple)) {
56-
return PropertyName.construct(mangledName)
57-
}
32+
override fun findImplicitPropertyName(member: AnnotatedMember): String? {
33+
if (!member.declaringClass.isKotlinClass()) return null
34+
35+
val name = member.name
36+
37+
return when (member) {
38+
is AnnotatedMethod -> if (member.parameterCount == 0) {
39+
// The reason for truncating after `-` is to truncate the random suffix
40+
// given after the value class accessor name.
41+
when {
42+
name.startsWith("get") -> name.takeIf { it.contains("-") }?.let { _ ->
43+
name.substringAfter("get")
44+
.replaceFirstChar { it.lowercase(Locale.getDefault()) }
45+
.substringBefore('-')
46+
}
47+
// since 2.15: support Kotlin's way of handling "isXxx" backed properties where
48+
// logical property name needs to remain "isXxx" and not become "xxx" as with Java Beans
49+
// (see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html and
50+
// https://github.com/FasterXML/jackson-databind/issues/2527 and
51+
// https://github.com/FasterXML/jackson-module-kotlin/issues/340
52+
// for details)
53+
name.startsWith("is") -> if (name.contains("-")) name.substringAfter("-") else name
54+
else -> null
55+
}
56+
} else null
57+
is AnnotatedParameter -> findKotlinParameterName(member)
58+
else -> null
5859
}
59-
return null
6060
}
6161

6262
@Suppress("UNCHECKED_CAST")

src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ParameterNameTests.kt

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,27 @@ class TestJacksonWithKotlin {
2525
val primaryAddress: String
2626
val wrongName: Boolean
2727
val createdDt: Date
28+
val isName: Boolean
2829

2930
fun validate(
3031
nameField: String = name,
3132
ageField: Int = age,
3233
addressField: String = primaryAddress,
3334
wrongNameField: Boolean = wrongName,
34-
createDtField: Date = createdDt
35+
createDtField: Date = createdDt,
36+
isNameField: Boolean = isName,
3537
) {
3638
assertThat(nameField, equalTo("Frank"))
3739
assertThat(ageField, equalTo(30))
3840
assertThat(addressField, equalTo("something here"))
3941
assertThat(wrongNameField, equalTo(true))
4042
assertThat(createDtField, equalTo(Date(1477419948000)))
43+
assertThat(isNameField, equalTo(false))
4144
}
4245
}
4346

44-
private val normalCasedJson = """{"name":"Frank","age":30,"primaryAddress":"something here","renamed":true,"createdDt":"2016-10-25T18:25:48.000+00:00"}"""
45-
private val pascalCasedJson = """{"Name":"Frank","Age":30,"PrimaryAddress":"something here","Renamed":true,"CreatedDt":"2016-10-25T18:25:48.000+00:00"}"""
47+
private val normalCasedJson = """{"name":"Frank","age":30,"primaryAddress":"something here","renamed":true,"createdDt":"2016-10-25T18:25:48.000+00:00","isName":false}"""
48+
private val pascalCasedJson = """{"Name":"Frank","Age":30,"PrimaryAddress":"something here","Renamed":true,"CreatedDt":"2016-10-25T18:25:48.000+00:00","IsName":false}"""
4649

4750
private val normalCasedMapper = jacksonObjectMapper()
4851
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
@@ -65,6 +68,7 @@ class TestJacksonWithKotlin {
6568

6669
override var primaryAddress: String = ""
6770
override var createdDt: Date = Date()
71+
override val isName: Boolean = false
6872
}
6973

7074
@Test fun NoFailWithDefaultAndSpecificConstructor() {
@@ -79,7 +83,8 @@ class TestJacksonWithKotlin {
7983
override val age: Int,
8084
override val primaryAddress: String,
8185
val renamed: Boolean,
82-
override val createdDt: Date
86+
override val createdDt: Date,
87+
override val isName: Boolean
8388
) : TestFields {
8489
@JsonIgnore
8590
override val wrongName = renamed // here for the test validation only
@@ -97,7 +102,8 @@ class TestJacksonWithKotlin {
97102
override val age: Int,
98103
override val primaryAddress: String,
99104
val renamed: Boolean,
100-
override val createdDt: Date
105+
override val createdDt: Date,
106+
override val isName: Boolean
101107
) : TestFields {
102108
@JsonIgnore
103109
override val wrongName = renamed // here for the test validation only
@@ -121,7 +127,8 @@ class TestJacksonWithKotlin {
121127
override val age: Int,
122128
override val primaryAddress: String,
123129
@JsonProperty("renamed") override val wrongName: Boolean,
124-
override val createdDt: Date
130+
override val createdDt: Date,
131+
override val isName: Boolean
125132
) : TestFields
126133

127134
@Test fun testDataClassWithExplicitJsonCreatorAndJsonProperty() {
@@ -141,7 +148,8 @@ class TestJacksonWithKotlin {
141148
override val age: Int,
142149
override val primaryAddress: String,
143150
@JsonProperty("renamed") override val wrongName: Boolean,
144-
override val createdDt: Date
151+
override val createdDt: Date,
152+
override val isName: Boolean
145153
) : TestFields
146154

147155
@Test fun testNormalClassWithJsonCreator() {
@@ -155,7 +163,8 @@ class TestJacksonWithKotlin {
155163
private class StateObjectWithPartialFieldsInConstructor(
156164
override val name: String,
157165
override val age: Int,
158-
override val primaryAddress: String
166+
override val primaryAddress: String,
167+
override val isName: Boolean
159168
) : TestFields {
160169
@JsonProperty("renamed") override var wrongName: Boolean = false
161170
override var createdDt: Date by Delegates.notNull()
@@ -176,7 +185,8 @@ class TestJacksonWithKotlin {
176185
override val age: Int,
177186
override val primaryAddress: String,
178187
@JsonProperty("renamed") override val wrongName: Boolean,
179-
override val createdDt: Date
188+
override val createdDt: Date,
189+
override val isName: Boolean
180190
) : TestFields
181191

182192
@Test fun testDataClassWithNonFieldParametersInConstructor() {
@@ -207,7 +217,8 @@ class TestJacksonWithKotlin {
207217
override val age: Int,
208218
override val primaryAddress: String,
209219
override val wrongName: Boolean,
210-
override val createdDt: Date
220+
override val createdDt: Date,
221+
override val isName: Boolean
211222
) : TestFields {
212223
var factoryUsed: Boolean = false
213224
companion object {
@@ -216,9 +227,10 @@ class TestJacksonWithKotlin {
216227
@JsonProperty("age") age: Int,
217228
@JsonProperty("primaryAddress") primaryAddress: String,
218229
@JsonProperty("renamed") wrongName: Boolean,
219-
@JsonProperty("createdDt") createdDt: Date
230+
@JsonProperty("createdDt") createdDt: Date,
231+
@JsonProperty("isName") isName: Boolean
220232
): StateObjectWithFactory {
221-
val obj = StateObjectWithFactory(nameThing, age, primaryAddress, wrongName, createdDt)
233+
val obj = StateObjectWithFactory(nameThing, age, primaryAddress, wrongName, createdDt, isName)
222234
obj.factoryUsed = true
223235
return obj
224236
}
@@ -236,17 +248,19 @@ class TestJacksonWithKotlin {
236248
val age: Int,
237249
val primaryAddress: String,
238250
val renamed: Boolean,
239-
val createdDt: Date
251+
val createdDt: Date,
252+
val isName: Boolean
240253
) {
241254
companion object {
242255
@JvmStatic @JsonCreator fun create(
243256
name: String,
244257
age: Int,
245258
primaryAddress: String,
246259
renamed: Boolean,
247-
createdDt: Date
260+
createdDt: Date,
261+
isName: Boolean
248262
): StateObjectWithFactoryNoParamAnnotations {
249-
return StateObjectWithFactoryNoParamAnnotations(name, age, primaryAddress, renamed, createdDt)
263+
return StateObjectWithFactoryNoParamAnnotations(name, age, primaryAddress, renamed, createdDt, isName)
250264
}
251265
}
252266
}
@@ -266,7 +280,8 @@ class TestJacksonWithKotlin {
266280
override val age: Int,
267281
override val primaryAddress: String,
268282
override val wrongName: Boolean,
269-
override val createdDt: Date
283+
override val createdDt: Date,
284+
override val isName: Boolean
270285
) : TestFields {
271286
var factoryUsed: Boolean = false
272287
private companion object Named {
@@ -275,9 +290,10 @@ class TestJacksonWithKotlin {
275290
@JsonProperty("age") age: Int,
276291
@JsonProperty("primaryAddress") primaryAddress: String,
277292
@JsonProperty("renamed") wrongName: Boolean,
278-
@JsonProperty("createdDt") createdDt: Date
293+
@JsonProperty("createdDt") createdDt: Date,
294+
@JsonProperty("isName") isName: Boolean
279295
): StateObjectWithFactoryOnNamedCompanion {
280-
val obj = StateObjectWithFactoryOnNamedCompanion(nameThing, age, primaryAddress, wrongName, createdDt)
296+
val obj = StateObjectWithFactoryOnNamedCompanion(nameThing, age, primaryAddress, wrongName, createdDt, isName)
281297
obj.factoryUsed = true
282298
return obj
283299
}

src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/failing/Github340.kt renamed to src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github340.kt

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
package com.fasterxml.jackson.module.kotlin.test.github.failing
1+
package com.fasterxml.jackson.module.kotlin.test.github
22

33
import com.fasterxml.jackson.databind.ObjectMapper
4-
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException
4+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
55
import com.fasterxml.jackson.module.kotlin.kotlinModule
66
import com.fasterxml.jackson.module.kotlin.readValue
7-
import com.fasterxml.jackson.module.kotlin.test.expectFailure
87
import org.junit.Test
98
import kotlin.test.assertEquals
109

@@ -21,15 +20,27 @@ class OwnerRequestTest {
2120

2221
@Test
2322
fun testDeserHit340() {
24-
expectFailure<UnrecognizedPropertyException>("GitHub #340 has been fixed!") {
25-
val value: IsField = jackson.readValue(json)
26-
assertEquals("Got a foo", value.foo)
27-
}
23+
val value: IsField = jackson.readValue(json)
24+
// Fixed
25+
assertEquals("Got a foo", value.foo)
2826
}
2927

3028
@Test
3129
fun testDeserWithoutIssue() {
3230
val value: NoIsField = jackson.readValue(json)
3331
assertEquals("Got a foo", value.foo)
3432
}
33+
34+
// A test case for isSetter to work, added with the fix for this issue.
35+
class IsSetter {
36+
lateinit var isFoo: String
37+
}
38+
39+
@Test
40+
fun isSetterTest() {
41+
val json = """{"isFoo":"bar"}"""
42+
val isSetter: IsSetter = jackson.readValue(json)
43+
44+
assertEquals("bar", isSetter.isFoo)
45+
}
3546
}

0 commit comments

Comments
 (0)