Skip to content

Commit

Permalink
Merge remote-tracking branch 'FasterXML/2.16'
Browse files Browse the repository at this point in the history
  • Loading branch information
k163377 committed Jul 28, 2023
2 parents 4a2fe74 + 423fc83 commit aacb2b8
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 33 deletions.
1 change: 1 addition & 0 deletions release-notes/CREDITS-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Contributors:

WrongWrong (@k163377)
* #687: Optimize and Refactor KotlinValueInstantiator.createFromObjectWith
* #686: Add KotlinPropertyNameAsImplicitName option
* #685: Streamline default value management for KotlinFeatures
* #684: Update Kotlin Version to 1.6
* #682: Remove MissingKotlinParameterException and replace with MismatchedInputException
Expand Down
3 changes: 3 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Co-maintainers:
#687: Optimize and Refactor KotlinValueInstantiator.createFromObjectWith.
This improves deserialization throughput about 1.3 ~ 1.5 times faster.
https://github.com/FasterXML/jackson-module-kotlin/pull/687#issuecomment-1637365799
#686: Added KotlinPropertyNameAsImplicitName feature to use Kotlin property names as implicit names for getters.
Enabling this feature eliminates some of the problems summarized in #630,
but also causes some behavioral changes and performance degradation.
#685: Streamline default value management for KotlinFeatures.
This improves the initialization cost of kotlin-module a little.
#684: Kotlin 1.5 has been deprecated and the minimum supported Kotlin version will be updated to 1.6.
Expand Down
20 changes: 18 additions & 2 deletions src/main/kotlin/tools/jackson/module/kotlin/KotlinFeature.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package tools.jackson.module.kotlin

import java.util.BitSet
import kotlin.math.pow

/**
* @see KotlinModule.Builder
Expand Down Expand Up @@ -42,7 +41,24 @@ enum class KotlinFeature(private val enabledByDefault: Boolean) {
* may contain null values after deserialization.
* Enabling it protects against this but has significant performance impact.
*/
StrictNullChecks(enabledByDefault = false);
StrictNullChecks(enabledByDefault = false),

/**
* By enabling this feature, the property name on Kotlin is used as the implicit name for the getter.
*
* By default, the getter name is used during serialization.
* This name may be different from the parameter/field name, in which case serialization results
* may be incorrect or annotations may malfunction.
* See [jackson-module-kotlin#630] for details.
*
* By enabling this feature, such malfunctions will not occur.
*
* On the other hand, enabling this option increases the amount of reflection processing,
* which may result in performance degradation for both serialization and deserialization.
* In addition, the adjustment of behavior using get:JvmName is disabled.
* Note also that this feature does not apply to setters.
*/
KotlinPropertyNameAsImplicitName(enabledByDefault = false);

internal val bitSet: BitSet = (1 shl ordinal).toBitSet()

Expand Down
21 changes: 13 additions & 8 deletions src/main/kotlin/tools/jackson/module/kotlin/KotlinModule.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package tools.jackson.module.kotlin

import java.util.*

import kotlin.reflect.KClass

import tools.jackson.databind.MapperFeature
import tools.jackson.databind.module.SimpleModule

import tools.jackson.module.kotlin.KotlinFeature.NullIsSameAsDefault
import tools.jackson.module.kotlin.KotlinFeature.NullToEmptyCollection
import tools.jackson.module.kotlin.KotlinFeature.NullToEmptyMap
import tools.jackson.module.kotlin.KotlinFeature.StrictNullChecks
import tools.jackson.module.kotlin.SingletonSupport.CANONICALIZE
import tools.jackson.module.kotlin.SingletonSupport.DISABLED
import java.util.*

private const val metadataFqName = "kotlin.Metadata"

Expand Down Expand Up @@ -55,8 +52,9 @@ class KotlinModule @Deprecated(
val nullToEmptyMap: Boolean = false,
val nullIsSameAsDefault: Boolean = false,
val singletonSupport: SingletonSupport = DISABLED,
val strictNullChecks: Boolean = false
) : SimpleModule(KotlinModule::class.java.name, tools.jackson.module.kotlin.PackageVersion.VERSION) {
val strictNullChecks: Boolean = false,
val useKotlinPropertyNameForGetter: Boolean = false
) : SimpleModule(KotlinModule::class.java.name, PackageVersion.VERSION) {
init {
if (!KotlinVersion.CURRENT.isAtLeast(1, 5)) {
// Kotlin 1.4 was deprecated when this process was introduced(jackson-module-kotlin 2.15).
Expand Down Expand Up @@ -103,7 +101,8 @@ class KotlinModule @Deprecated(
builder.isEnabled(KotlinFeature.SingletonSupport) -> CANONICALIZE
else -> DISABLED
},
builder.isEnabled(StrictNullChecks)
builder.isEnabled(StrictNullChecks),
builder.isEnabled(KotlinFeature.KotlinPropertyNameAsImplicitName)
)

companion object {
Expand Down Expand Up @@ -132,7 +131,13 @@ class KotlinModule @Deprecated(
}

context.insertAnnotationIntrospector(KotlinAnnotationIntrospector(context, cache, nullToEmptyCollection, nullToEmptyMap, nullIsSameAsDefault))
context.appendAnnotationIntrospector(KotlinNamesAnnotationIntrospector(this, cache, ignoredClassesForImplyingJsonCreator))
context.appendAnnotationIntrospector(
KotlinNamesAnnotationIntrospector(
this,
cache,
ignoredClassesForImplyingJsonCreator,
useKotlinPropertyNameForGetter)
)

context.addDeserializers(KotlinDeserializers())
context.addKeyDeserializers(KotlinKeyDeserializers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ package tools.jackson.module.kotlin
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonCreator.Mode
import com.fasterxml.jackson.annotation.JsonProperty
import tools.jackson.databind.PropertyName
import tools.jackson.databind.cfg.MapperConfig
import tools.jackson.databind.introspect.Annotated
import tools.jackson.databind.introspect.AnnotatedConstructor
import tools.jackson.databind.introspect.AnnotatedField
import tools.jackson.databind.introspect.AnnotatedMember
import tools.jackson.databind.introspect.AnnotatedMethod
import tools.jackson.databind.introspect.AnnotatedParameter
import tools.jackson.databind.introspect.NopAnnotationIntrospector
import tools.jackson.databind.util.BeanUtil
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import java.util.Locale
Expand All @@ -25,34 +22,57 @@ import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
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<KClass<*>>) : NopAnnotationIntrospector() {
internal class KotlinNamesAnnotationIntrospector(
val module: KotlinModule,
val cache: ReflectionCache,
val ignoredClassesForImplyingJsonCreator: Set<KClass<*>>,
val useKotlinPropertyNameForGetter: Boolean
) : NopAnnotationIntrospector() {
private fun getterNameFromJava(member: AnnotatedMethod): String? {
val name = member.name

// The reason for truncating after `-` is to truncate the random suffix
// given after the value class accessor name.
return 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
}
}

private fun getterNameFromKotlin(member: AnnotatedMethod): String? {
val getter = member.member

return member.member.declaringClass.takeIf { it.isKotlinClass() }?.let { clazz ->
clazz.kotlin.memberProperties.find { it.javaGetter == getter }
?.let { it.name }
}
}

// since 2.4
override fun findImplicitPropertyName(config: MapperConfig<*>, 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
}
if (useKotlinPropertyNameForGetter) {
// Fall back to default if it is a getter-like function
getterNameFromKotlin(member) ?: getterNameFromJava(member)
} else getterNameFromJava(member)
} else null
is AnnotatedParameter -> findKotlinParameterName(member)
else -> null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package tools.jackson.module.kotlin.test.github

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinFeature
import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.junit.Test
import kotlin.test.assertEquals

class Github630 {
private val mapper = ObjectMapper()
.registerModule(KotlinModule.Builder().enable(KotlinFeature.KotlinPropertyNameAsImplicitName).build())!!

data class Dto(
// from #570, #603
val FOO: Int = 0,
val bAr: Int = 0,
@JsonProperty("b")
val BAZ: Int = 0,
@JsonProperty("q")
val qUx: Int = 0,
// from #71
internal val quux: Int = 0,
// from #434
val `corge-corge`: Int = 0,
// additional
@get:JvmName("aaa")
val grault: Int = 0
)

@Test
fun test() {
val dto = Dto()

assertEquals(
"""{"FOO":0,"bAr":0,"b":0,"q":0,"quux":0,"corge-corge":0,"grault":0}""",
mapper.writeValueAsString(dto)
)
}
}

0 comments on commit aacb2b8

Please sign in to comment.