From 844ce3538f31317d7d34e15e80e719be12e814c1 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Tue, 4 Jan 2022 01:14:55 +0900 Subject: [PATCH 1/7] In order to reduce processing costs, not rewrite if not necessary. --- .../fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt index 0aaa0a758..d02c6b855 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt @@ -7,6 +7,6 @@ internal class ConstructorValueCreator(override val callable: KFunction) : override val accessible: Boolean = callable.isAccessible init { - callable.isAccessible = true + if (!accessible) callable.isAccessible = true } } From b01c4bdcac7a33444f67f2ca3156041891d42904 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Tue, 4 Jan 2022 01:23:42 +0900 Subject: [PATCH 2/7] Fix the call would fail if callable.isAccessible was false. --- .../fasterxml/jackson/module/kotlin/MethodValueCreator.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt index 986ec0769..15fe62674 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt @@ -23,11 +23,15 @@ internal class MethodValueCreator private constructor( // abort, we have some unknown case here if (!possibleCompanion.isCompanion) return null + val initialCallableAccessible = callable.isAccessible + if (!initialCallableAccessible) callable.isAccessible = true + val (companionObjectInstance: Any, accessible: Boolean) = try { // throws ex val instance = possibleCompanion.objectInstance!! + // If an instance of the companion object can be obtained, accessibility depends on the KFunction - instance to callable.isAccessible + instance to initialCallableAccessible } catch (ex: IllegalAccessException) { // fallback for when an odd access exception happens through Kotlin reflection possibleCompanion.java.enclosingClass.fields From c0a58aba50b7ed3420c42042f72ed571ecd5305d Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Tue, 4 Jan 2022 01:32:09 +0900 Subject: [PATCH 3/7] Fix the call would fail if the companion object was private. --- .../com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt index 15fe62674..c5ca99fd1 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt @@ -34,7 +34,7 @@ internal class MethodValueCreator private constructor( instance to initialCallableAccessible } catch (ex: IllegalAccessException) { // fallback for when an odd access exception happens through Kotlin reflection - possibleCompanion.java.enclosingClass.fields + possibleCompanion.java.enclosingClass.declaredFields .firstOrNull { it.type.kotlin.isCompanion } ?.let { it.isAccessible = true From 3428cd34202e6acba09f8a28239cd7c3e60851c0 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Tue, 4 Jan 2022 01:32:19 +0900 Subject: [PATCH 4/7] Fix test case --- .../jackson/module/kotlin/test/ParameterNameTests.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 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 64169d4f0..faf739c28 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 @@ -199,8 +199,8 @@ class TestJacksonWithKotlin { private class StateObjectWithFactoryOnNamedCompanion private constructor (override val name: String, override val age: Int, override val primaryAddress: String, override val wrongName: Boolean, override val createdDt: Date) : TestFields { var factoryUsed: Boolean = false - companion object Named { - @JvmStatic @JsonCreator fun create(@JsonProperty("name") nameThing: String, @JsonProperty("age") age: Int, @JsonProperty("primaryAddress") primaryAddress: String, @JsonProperty("renamed") wrongName: Boolean, @JsonProperty("createdDt") createdDt: Date): StateObjectWithFactoryOnNamedCompanion { + private companion object Named { + @JvmStatic @JsonCreator private fun create(@JsonProperty("name") nameThing: String, @JsonProperty("age") age: Int, @JsonProperty("primaryAddress") primaryAddress: String, @JsonProperty("renamed") wrongName: Boolean, @JsonProperty("createdDt") createdDt: Date): StateObjectWithFactoryOnNamedCompanion { val obj = StateObjectWithFactoryOnNamedCompanion(nameThing, age, primaryAddress, wrongName, createdDt) obj.factoryUsed = true return obj From 5eca9cd70fa125a94a822ba0f7c92478512e228a Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Tue, 4 Jan 2022 21:01:48 +0900 Subject: [PATCH 5/7] add comments --- .../fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt | 1 + .../com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt index d02c6b855..4f7116798 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ConstructorValueCreator.kt @@ -7,6 +7,7 @@ internal class ConstructorValueCreator(override val callable: KFunction) : override val accessible: Boolean = callable.isAccessible init { + // To prevent the call from failing, save the initial value and then rewrite the flag. if (!accessible) callable.isAccessible = true } } diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt index c5ca99fd1..96f489a10 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/MethodValueCreator.kt @@ -23,6 +23,7 @@ internal class MethodValueCreator private constructor( // abort, we have some unknown case here if (!possibleCompanion.isCompanion) return null + // To prevent the call from failing, save the initial value and then rewrite the flag. val initialCallableAccessible = callable.isAccessible if (!initialCallableAccessible) callable.isAccessible = true From b19c44f9461f4e6b4a26bc64dd8268ed6628f340 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Tue, 4 Jan 2022 21:04:35 +0900 Subject: [PATCH 6/7] formatting --- .../module/kotlin/test/ParameterNameTests.kt | 118 ++++++++++++++---- 1 file changed, 97 insertions(+), 21 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 faf739c28..a27311c51 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 @@ -26,7 +26,13 @@ class TestJacksonWithKotlin { val wrongName: Boolean val createdDt: Date - fun validate(nameField: String = name, ageField: Int = age, addressField: String = primaryAddress, wrongNameField: Boolean = wrongName, createDtField: Date = createdDt) { + fun validate( + nameField: String = name, + ageField: Int = age, + addressField: String = primaryAddress, + wrongNameField: Boolean = wrongName, + createDtField: Date = createdDt + ) { assertThat(nameField, equalTo("Frank")) assertThat(ageField, equalTo(30)) assertThat(addressField, equalTo("something here")) @@ -50,7 +56,10 @@ class TestJacksonWithKotlin { // ================== - private class DefaultAndSpecificConstructor(override var name: String = "", override var age: Int = 0) : TestFields { + private class DefaultAndSpecificConstructor( + override var name: String = "", + override var age: Int = 0 + ) : TestFields { @JsonProperty("renamed") override var wrongName: Boolean = false @@ -65,7 +74,13 @@ class TestJacksonWithKotlin { // ================== - private class NoFailWithoutJsonCreator(override val name: String, override val age: Int, override val primaryAddress: String, val renamed: Boolean, override val createdDt: Date) : TestFields { + private class NoFailWithoutJsonCreator( + override val name: String, + override val age: Int, + override val primaryAddress: String, + val renamed: Boolean, + override val createdDt: Date + ) : TestFields { @JsonIgnore override val wrongName = renamed // here for the test validation only } @@ -77,7 +92,13 @@ class TestJacksonWithKotlin { // ================== - private data class StateObjectAsDataClassExplicitJsonCreator @JsonCreator constructor(override val name: String, override val age: Int, override val primaryAddress: String, val renamed: Boolean, override val createdDt: Date) : TestFields { + private data class StateObjectAsDataClassExplicitJsonCreator @JsonCreator constructor( + override val name: String, + override val age: Int, + override val primaryAddress: String, + val renamed: Boolean, + override val createdDt: Date + ) : TestFields { @JsonIgnore override val wrongName = renamed // here for the test validation only } @@ -95,11 +116,18 @@ class TestJacksonWithKotlin { // ================== - private data class StateObjectAsDataClassWithJsonCreatorAndJsonProperty @JsonCreator constructor(override val name: String, override val age: Int, override val primaryAddress: String, @JsonProperty("renamed") override val wrongName: Boolean, override val createdDt: Date) : TestFields + private data class StateObjectAsDataClassWithJsonCreatorAndJsonProperty @JsonCreator constructor( + override val name: String, + override val age: Int, + override val primaryAddress: String, + @JsonProperty("renamed") override val wrongName: Boolean, + override val createdDt: Date + ) : TestFields @Test fun testDataClassWithExplicitJsonCreatorAndJsonProperty() { // data class with JsonCreator and JsonProperty - val stateObj = normalCasedMapper.readValue(normalCasedJson) + val stateObj = normalCasedMapper + .readValue(normalCasedJson) stateObj.validate() val test1out = normalCasedMapper.writeValueAsString(stateObj) @@ -108,7 +136,13 @@ class TestJacksonWithKotlin { // ================== - private class StateObjectAsNormalClass @JsonCreator constructor(override val name: String, override val age: Int, override val primaryAddress: String, @JsonProperty("renamed") override val wrongName: Boolean, override val createdDt: Date) : TestFields + private class StateObjectAsNormalClass @JsonCreator constructor( + override val name: String, + override val age: Int, + override val primaryAddress: String, + @JsonProperty("renamed") override val wrongName: Boolean, + override val createdDt: Date + ) : TestFields @Test fun testNormalClassWithJsonCreator() { // normal class @@ -118,7 +152,11 @@ class TestJacksonWithKotlin { // ================== - private class StateObjectWithPartialFieldsInConstructor(override val name: String, override val age: Int, override val primaryAddress: String) : TestFields { + private class StateObjectWithPartialFieldsInConstructor( + override val name: String, + override val age: Int, + override val primaryAddress: String + ) : TestFields { @JsonProperty("renamed") override var wrongName: Boolean = false override var createdDt: Date by Delegates.notNull() } @@ -131,13 +169,15 @@ class TestJacksonWithKotlin { // ================== - private class StateObjectAsDataClassConfusingConstructor constructor (@Suppress("UNUSED_PARAMETER") nonField: String?, - override val name: String, - @Suppress("UNUSED_PARAMETER") yearOfBirth: Int, - override val age: Int, - override val primaryAddress: String, - @JsonProperty("renamed") override val wrongName: Boolean, - override val createdDt: Date) : TestFields + private class StateObjectAsDataClassConfusingConstructor constructor( + @Suppress("UNUSED_PARAMETER") nonField: String?, + override val name: String, + @Suppress("UNUSED_PARAMETER") yearOfBirth: Int, + override val age: Int, + override val primaryAddress: String, + @JsonProperty("renamed") override val wrongName: Boolean, + override val createdDt: Date + ) : TestFields @Test fun testDataClassWithNonFieldParametersInConstructor() { // data class with non fields appearing as parameters in constructor, this works but null values or defaults for primitive types are passed to @@ -162,10 +202,22 @@ class TestJacksonWithKotlin { // ================== - private class StateObjectWithFactory private constructor (override val name: String, override val age: Int, override val primaryAddress: String, override val wrongName: Boolean, override val createdDt: Date) : TestFields { + private class StateObjectWithFactory private constructor( + override val name: String, + override val age: Int, + override val primaryAddress: String, + override val wrongName: Boolean, + override val createdDt: Date + ) : TestFields { var factoryUsed: Boolean = false companion object { - @JvmStatic @JsonCreator fun create(@JsonProperty("name") nameThing: String, @JsonProperty("age") age: Int, @JsonProperty("primaryAddress") primaryAddress: String, @JsonProperty("renamed") wrongName: Boolean, @JsonProperty("createdDt") createdDt: Date): StateObjectWithFactory { + @JvmStatic @JsonCreator fun create( + @JsonProperty("name") nameThing: String, + @JsonProperty("age") age: Int, + @JsonProperty("primaryAddress") primaryAddress: String, + @JsonProperty("renamed") wrongName: Boolean, + @JsonProperty("createdDt") createdDt: Date + ): StateObjectWithFactory { val obj = StateObjectWithFactory(nameThing, age, primaryAddress, wrongName, createdDt) obj.factoryUsed = true return obj @@ -179,9 +231,21 @@ class TestJacksonWithKotlin { assertThat(stateObj.factoryUsed, equalTo(true)) } - private class StateObjectWithFactoryNoParamAnnotations(val name: String, val age: Int, val primaryAddress: String, val renamed: Boolean, val createdDt: Date) { + private class StateObjectWithFactoryNoParamAnnotations( + val name: String, + val age: Int, + val primaryAddress: String, + val renamed: Boolean, + val createdDt: Date + ) { companion object { - @JvmStatic @JsonCreator fun create(name: String, age: Int, primaryAddress: String, renamed: Boolean, createdDt: Date): StateObjectWithFactoryNoParamAnnotations { + @JvmStatic @JsonCreator fun create( + name: String, + age: Int, + primaryAddress: String, + renamed: Boolean, + createdDt: Date + ): StateObjectWithFactoryNoParamAnnotations { return StateObjectWithFactoryNoParamAnnotations(name, age, primaryAddress, renamed, createdDt) } } @@ -197,10 +261,22 @@ class TestJacksonWithKotlin { } } - private class StateObjectWithFactoryOnNamedCompanion private constructor (override val name: String, override val age: Int, override val primaryAddress: String, override val wrongName: Boolean, override val createdDt: Date) : TestFields { + private class StateObjectWithFactoryOnNamedCompanion private constructor( + override val name: String, + override val age: Int, + override val primaryAddress: String, + override val wrongName: Boolean, + override val createdDt: Date + ) : TestFields { var factoryUsed: Boolean = false private companion object Named { - @JvmStatic @JsonCreator private fun create(@JsonProperty("name") nameThing: String, @JsonProperty("age") age: Int, @JsonProperty("primaryAddress") primaryAddress: String, @JsonProperty("renamed") wrongName: Boolean, @JsonProperty("createdDt") createdDt: Date): StateObjectWithFactoryOnNamedCompanion { + @JvmStatic @JsonCreator private fun create( + @JsonProperty("name") nameThing: String, + @JsonProperty("age") age: Int, + @JsonProperty("primaryAddress") primaryAddress: String, + @JsonProperty("renamed") wrongName: Boolean, + @JsonProperty("createdDt") createdDt: Date + ): StateObjectWithFactoryOnNamedCompanion { val obj = StateObjectWithFactoryOnNamedCompanion(nameThing, age, primaryAddress, wrongName, createdDt) obj.factoryUsed = true return obj From 9aa88e51236f115148128b22c934c62398e23a4b Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Fri, 7 Jan 2022 20:39:39 +0900 Subject: [PATCH 7/7] Fixed to public A `private` and `JvmStatic` function was broken in `Kotlin 1.5.x`. See GitHub Comment below for details. https://github.com/FasterXML/jackson-module-kotlin/pull/533#issuecomment-1007337661 --- .../fasterxml/jackson/module/kotlin/test/ParameterNameTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a27311c51..b3dd29a57 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 @@ -270,7 +270,7 @@ class TestJacksonWithKotlin { ) : TestFields { var factoryUsed: Boolean = false private companion object Named { - @JvmStatic @JsonCreator private fun create( + @JvmStatic @JsonCreator fun create( @JsonProperty("name") nameThing: String, @JsonProperty("age") age: Int, @JsonProperty("primaryAddress") primaryAddress: String,