Skip to content

Commit f1ec80b

Browse files
authored
Merge pull request #743 from k163377/fix-vararg
Fix handling of vararg deserialization
2 parents c49ac9a + fd8ffdf commit f1ec80b

File tree

6 files changed

+128
-5
lines changed

6 files changed

+128
-5
lines changed

release-notes/CREDITS-2.x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Contributors:
1818
# 2.17.0 (not yet released)
1919

2020
WrongWrong (@k163377)
21+
* #743: Fix handling of vararg deserialization.
2122
* #742: Minor performance improvements to NullToEmptyCollection/Map.
2223
* #741: Changed to allow KotlinFeature to be set in the function that registers a KotlinModule.
2324
* #740: Reduce conversion cache from Executable to KFunction.

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Co-maintainers:
1818

1919
2.17.0 (not yet released)
2020

21+
* #743: The handling of deserialization using vararg arguments has been improved to allow deserialization even when the input to the vararg argument is undefined.
22+
In addition, vararg arguments are now reported as non-required.
2123
#742: Minor performance improvements to NullToEmptyCollection/Map.
2224
#741: Changed to allow KotlinFeature to be set in the function that registers a KotlinModule.
2325
The `jacksonObjectMapper {}` and `registerKotlinModule {}` lambdas allow configuration for KotlinModule.

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,12 @@ internal class KotlinAnnotationIntrospector(
207207
private fun KFunction<*>.isParameterRequired(index: Int): Boolean {
208208
val param = parameters[index]
209209
val paramType = param.type
210-
val javaType = paramType.javaType
211-
val isPrimitive = when (javaType) {
210+
val isPrimitive = when (val javaType = paramType.javaType) {
212211
is Class<*> -> javaType.isPrimitive
213212
else -> false
214213
}
215214

216-
return !paramType.isMarkedNullable && !param.isOptional &&
215+
return !paramType.isMarkedNullable && !param.isOptional && !param.isVararg &&
217216
!(isPrimitive && !context.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES))
218217
}
219218

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ internal class KotlinValueInstantiator(
7474
tempParamVal
7575
} else {
7676
when {
77-
paramDef.isOptional -> return@forEachIndexed
77+
paramDef.isOptional || paramDef.isVararg -> return@forEachIndexed
7878
// do not try to create any object if it is nullable and the value is missing
7979
paramType.isMarkedNullable -> null
8080
// Primitive types always try to get from a buffer, considering several settings

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class TestPropertyRequiredness {
7676

7777
// ---
7878

79-
private data class TestDataClass(
79+
private class TestDataClass(
8080
val a: Int,
8181
val b: Int?,
8282
val c: Int = 5,
@@ -85,6 +85,7 @@ class TestPropertyRequiredness {
8585
val f: TestParamClass?,
8686
val g: TestParamClass = TestParamClass(),
8787
val h: TestParamClass? = TestParamClass(),
88+
vararg val i: Int,
8889
@JsonProperty("x", required = true) val x: Int?, // TODO: either error in test case with this not being on the property getter, or error in introspection not seeing this on the constructor parameter
8990
@get:JsonProperty("z", required = true) val z: Int
9091
)
@@ -117,6 +118,9 @@ class TestPropertyRequiredness {
117118
"h".isOptionalForSerializationOf(testClass, mapper)
118119
"h".isOptionalForDeserializationOf(testClass, mapper)
119120

121+
"i".isRequiredForSerializationOf(testClass, mapper)
122+
"i".isOptionalForDeserializationOf(testClass, mapper)
123+
120124
"x".isRequiredForDeserializationOf(testClass, mapper)
121125
"x".isOptionalForSerializationOf(testClass, mapper)
122126

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.fasterxml.jackson.module.kotlin.test
2+
3+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
4+
import com.fasterxml.jackson.module.kotlin.readValue
5+
import junit.framework.TestCase.assertEquals
6+
import junit.framework.TestCase.assertTrue
7+
import org.junit.Ignore
8+
import org.junit.experimental.runners.Enclosed
9+
import org.junit.runner.RunWith
10+
import kotlin.test.Test
11+
12+
// from https://github.com/ProjectMapK/jackson-module-kogera/blob/0631cd3b07c7fb6971a00ac1f6811b4367a1720e/src/test/kotlin/io/github/projectmapk/jackson/module/kogera/zIntegration/deser/VarargTest.kt#L1
13+
@RunWith(Enclosed::class)
14+
class VarargDeserTest {
15+
@Ignore
16+
companion object {
17+
val mapper = jacksonObjectMapper()
18+
}
19+
20+
@Ignore
21+
class OnlyVararg(vararg val v: Int)
22+
23+
class OnlyVarargTest {
24+
@Test
25+
fun hasArgs() {
26+
val r = mapper.readValue<OnlyVararg>("""{"v":[1,2,3]}""")
27+
assertEquals(listOf(1, 2, 3), r.v.asList())
28+
}
29+
30+
@Test
31+
fun empty() {
32+
val r = mapper.readValue<OnlyVararg>("""{"v":[]}""")
33+
assertTrue(r.v.isEmpty())
34+
}
35+
36+
@Test
37+
fun undefined() {
38+
val r = mapper.readValue<OnlyVararg>("""{}""")
39+
assertTrue(r.v.isEmpty())
40+
}
41+
}
42+
43+
@Ignore
44+
class HeadVararg(vararg val v: Int?, val i: Int)
45+
46+
class HeadVarargTest {
47+
@Test
48+
fun hasArgs() {
49+
val r = mapper.readValue<HeadVararg>("""{"i":0,"v":[1,2,null]}""")
50+
assertEquals(listOf(1, 2, null), r.v.asList())
51+
assertEquals(0, r.i)
52+
}
53+
54+
@Test
55+
fun empty() {
56+
val r = mapper.readValue<HeadVararg>("""{"i":0,"v":[]}""")
57+
assertTrue(r.v.isEmpty())
58+
assertEquals(0, r.i)
59+
}
60+
61+
@Test
62+
fun undefined() {
63+
val r = mapper.readValue<HeadVararg>("""{"i":0}""")
64+
assertTrue(r.v.isEmpty())
65+
assertEquals(0, r.i)
66+
}
67+
}
68+
69+
@Ignore
70+
class TailVararg(val i: Int, vararg val v: String)
71+
72+
class TailVarargTest {
73+
@Test
74+
fun hasArgs() {
75+
val r = mapper.readValue<TailVararg>("""{"i":0,"v":["foo","bar","baz"]}""")
76+
assertEquals(listOf("foo", "bar", "baz"), r.v.asList())
77+
assertEquals(0, r.i)
78+
}
79+
80+
@Test
81+
fun empty() {
82+
val r = mapper.readValue<TailVararg>("""{"i":0,"v":[]}""")
83+
assertTrue(r.v.isEmpty())
84+
assertEquals(0, r.i)
85+
}
86+
87+
@Test
88+
fun undefined() {
89+
val r = mapper.readValue<TailVararg>("""{"i":0}""")
90+
assertTrue(r.v.isEmpty())
91+
assertEquals(0, r.i)
92+
}
93+
}
94+
95+
@Ignore
96+
class HasDefaultVararg(vararg val v: String? = arrayOf("foo", "bar"))
97+
98+
class HasDefaultVarargTest {
99+
@Test
100+
fun hasArgs() {
101+
val r = mapper.readValue<HasDefaultVararg>("""{"v":["foo","bar",null]}""")
102+
assertEquals(listOf("foo", "bar", null), r.v.asList())
103+
}
104+
105+
@Test
106+
fun empty() {
107+
val r = mapper.readValue<HasDefaultVararg>("""{"v":[]}""")
108+
assertTrue(r.v.isEmpty())
109+
}
110+
111+
@Test
112+
fun undefined() {
113+
val r = mapper.readValue<HasDefaultVararg>("""{}""")
114+
assertEquals(listOf("foo", "bar"), r.v.asList())
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)