diff --git a/pom.xml b/pom.xml index 0ef4d2936..9a580a9ba 100644 --- a/pom.xml +++ b/pom.xml @@ -81,8 +81,9 @@ - com.fasterxml.jackson.core + com.github.wplong11 jackson-databind + 02f3fd2988 @@ -253,5 +254,9 @@ true + + jitpack.io + https://jitpack.io + diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt index a9b53951d..c822522b6 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt @@ -159,6 +159,10 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon private fun KFunction<*>.isSetterLike(): Boolean = parameters.size == 2 && returnType == UNIT_TYPE private fun AnnotatedParameter.hasRequiredMarker(): Boolean? { + if (rawType.isKotlinDefaultConstructorMarker) { + return false + } + val member = this.member val byAnnotation = this.getAnnotation(JsonProperty::class.java)?.required 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 858ceb7e7..383d09c08 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt @@ -2,16 +2,12 @@ package com.fasterxml.jackson.module.kotlin import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.PropertyName -import com.fasterxml.jackson.databind.cfg.MapperConfig import com.fasterxml.jackson.databind.introspect.Annotated import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor -import com.fasterxml.jackson.databind.introspect.AnnotatedField import com.fasterxml.jackson.databind.introspect.AnnotatedMember import com.fasterxml.jackson.databind.introspect.AnnotatedMethod import com.fasterxml.jackson.databind.introspect.AnnotatedParameter import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector -import com.fasterxml.jackson.databind.util.BeanUtil import java.lang.reflect.Constructor import java.lang.reflect.Method import java.util.Locale @@ -67,6 +63,10 @@ internal class KotlinNamesAnnotationIntrospector( // since 2.4 override fun findImplicitPropertyName(member: AnnotatedMember): String? { + if (member.rawType.isKotlinDefaultConstructorMarker) { + return "DefaultConstructorMarker" + } + if (!member.declaringClass.isKotlinClass()) return null return when (member) { @@ -118,23 +118,17 @@ internal class KotlinNamesAnnotationIntrospector( if (member is AnnotatedConstructor && member.isKotlinConstructorWithParameters()) cache.checkConstructorIsCreatorAnnotated(member) { hasCreatorAnnotation(it) } else - false + member.isJvmInlineClassSyntheticBoxingFunction + + private val Annotated.isJvmInlineClassSyntheticBoxingFunction: Boolean + get() = name == "box-impl" @Suppress("UNCHECKED_CAST") private fun findKotlinParameterName(param: AnnotatedParameter): String? { return if (param.declaringClass.isKotlinClass()) { val member = param.owner.member if (member is Constructor<*>) { - val ctor = (member as Constructor) - val ctorParmCount = ctor.parameterTypes.size - val ktorParmCount = try { ctor.kotlinFunction?.parameters?.size ?: 0 } - catch (ex: KotlinReflectionInternalError) { 0 } - catch (ex: UnsupportedOperationException) { 0 } - if (ktorParmCount > 0 && ktorParmCount == ctorParmCount) { - ctor.kotlinFunction?.parameters?.get(param.index)?.name - } else { - null - } + member.kotlinFunction?.parameters?.elementAtOrNull(param.index)?.name } else if (member is Method) { try { val temp = member.kotlinFunction diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt index 47f3683c1..839d7fcab 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinValueInstantiator.kt @@ -138,6 +138,16 @@ internal class KotlinValueInstantiator( numCallableParameters++ } + val isJvmInlineClassSyntheticConstructor: Boolean = + numCallableParameters == jsonParamValueList.size - 1 + && valueCreator is ConstructorValueCreator + && props.last().type.rawClass.isKotlinDefaultConstructorMarker + if (isJvmInlineClassSyntheticConstructor) { + jsonParamValueList[numCallableParameters] = null + callableParameters[numCallableParameters] = null + numCallableParameters++ + } + return if (numCallableParameters == jsonParamValueList.size && valueCreator is ConstructorValueCreator) { // we didn't do anything special with default parameters, do a normal call super.createFromObjectWith(ctxt, jsonParamValueList) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectJvmMapping.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectJvmMapping.kt new file mode 100644 index 000000000..67a3f59c4 --- /dev/null +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectJvmMapping.kt @@ -0,0 +1,15 @@ +package com.fasterxml.jackson.module.kotlin + +/** + * Identification class for synthetic constructor generated for default arguments and value classes. + */ +private val DEFAULT_CONSTRUCTOR_MARKER: Class<*> = try { + Class.forName("kotlin.jvm.internal.DefaultConstructorMarker") +} catch (ex: ClassNotFoundException) { + throw IllegalStateException( + "DefaultConstructorMarker not on classpath. Make sure the Kotlin stdlib is on the classpath." + ) +} + +val Class<*>.isKotlinDefaultConstructorMarker: Boolean + get() = this == DEFAULT_CONSTRUCTOR_MARKER diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ValueClassTest.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ValueClassTest.kt new file mode 100644 index 000000000..0232c9314 --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/ValueClassTest.kt @@ -0,0 +1,68 @@ +package com.fasterxml.jackson.module.kotlin.test + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.kotlinModule +import com.fasterxml.jackson.module.kotlin.readValue +import org.junit.Assert +import org.junit.Test + +class ValueClassTest { + @JvmInline + value class UserId(val rawValue: String) + + data class User( + val id: UserId, + val name: String, + ) + + data class Customer( + @JsonProperty("customerId") + val id: UserId, + @JsonProperty("customerName") + val name: String, + ) + + @Test + fun `deserialize value class correctly`() { + val json: String = """ + "1111" + """.trimIndent() + + val deserialized: UserId = createMapper().readValue(json) + + Assert.assertEquals("1111", deserialized.rawValue) + } + + @Test + fun `deserialize complex object with value class`() { + val json: String = """ + { + "id": "1111", + "name": "foo" + } + """.trimIndent() + + val deserialized: User = createMapper().readValue(json) + + Assert.assertEquals("1111", deserialized.id.rawValue) + } + + @Test + fun `deserialize complex annotated object with value class`() { + val json: String = """ + { + "customerId": "1111", + "customerName": "foo" + } + """.trimIndent() + + val deserialized: Customer = createMapper().readValue(json) + + Assert.assertEquals("1111", deserialized.id.rawValue) + } + + private fun createMapper(): ObjectMapper { + return ObjectMapper().registerModule(kotlinModule()) + } +}