Skip to content

Commit 1b4f5ac

Browse files
authored
Merge pull request #515 from k163377/add-unsigned-key-deser
Add `KeyDeserializers` for `unsigned integers`
2 parents 180e9a1 + 17e8243 commit 1b4f5ac

File tree

8 files changed

+185
-11
lines changed

8 files changed

+185
-11
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@file:Suppress("EXPERIMENTAL_API_USAGE")
2-
31
package com.fasterxml.jackson.module.kotlin
42

53
import com.fasterxml.jackson.core.JsonParser
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.fasterxml.jackson.module.kotlin
2+
3+
import com.fasterxml.jackson.core.JsonToken
4+
import com.fasterxml.jackson.core.exc.InputCoercionException
5+
import com.fasterxml.jackson.databind.*
6+
import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer
7+
import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers
8+
9+
// The reason why key is treated as nullable is to match the tentative behavior of StdKeyDeserializer.
10+
// If StdKeyDeserializer is modified, need to modify this too.
11+
12+
internal object UByteKeyDeserializer : StdKeyDeserializer(TYPE_SHORT, UByte::class.java) {
13+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UByte? = super.deserializeKey(key, ctxt)
14+
?.let {
15+
(it as Short).asUByte() ?: throw InputCoercionException(
16+
null,
17+
"Numeric value (${key}) out of range of UByte (0 - ${UByte.MAX_VALUE}).",
18+
JsonToken.VALUE_NUMBER_INT,
19+
UByte::class.java
20+
)
21+
}
22+
}
23+
24+
internal object UShortKeyDeserializer : StdKeyDeserializer(TYPE_INT, UShort::class.java) {
25+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UShort? = super.deserializeKey(key, ctxt)
26+
?.let {
27+
(it as Int).asUShort() ?: throw InputCoercionException(
28+
null,
29+
"Numeric value (${key}) out of range of UShort (0 - ${UShort.MAX_VALUE}).",
30+
JsonToken.VALUE_NUMBER_INT,
31+
UShort::class.java
32+
)
33+
}
34+
}
35+
36+
internal object UIntKeyDeserializer : StdKeyDeserializer(TYPE_LONG, UInt::class.java) {
37+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): UInt? = super.deserializeKey(key, ctxt)
38+
?.let {
39+
(it as Long).asUInt() ?: throw InputCoercionException(
40+
null,
41+
"Numeric value (${key}) out of range of UInt (0 - ${UInt.MAX_VALUE}).",
42+
JsonToken.VALUE_NUMBER_INT,
43+
UInt::class.java
44+
)
45+
}
46+
}
47+
48+
// kind parameter is dummy.
49+
internal object ULongKeyDeserializer : StdKeyDeserializer(TYPE_LONG, ULong::class.java) {
50+
override fun deserializeKey(key: String?, ctxt: DeserializationContext): ULong? = key?.let {
51+
it.toBigInteger().asULong() ?: throw InputCoercionException(
52+
null,
53+
"Numeric value (${key}) out of range of ULong (0 - ${ULong.MAX_VALUE}).",
54+
JsonToken.VALUE_NUMBER_INT,
55+
ULong::class.java
56+
)
57+
}
58+
}
59+
60+
internal object KotlinKeyDeserializers : StdKeyDeserializers() {
61+
override fun findKeyDeserializer(
62+
type: JavaType,
63+
config: DeserializationConfig?,
64+
beanDesc: BeanDescription?
65+
): KeyDeserializer? = when (type.rawClass) {
66+
UByte::class.java -> UByteKeyDeserializer
67+
UShort::class.java -> UShortKeyDeserializer
68+
UInt::class.java -> UIntKeyDeserializer
69+
ULong::class.java -> ULongKeyDeserializer
70+
else -> null
71+
}
72+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ class KotlinModule @Deprecated(
122122
context.appendAnnotationIntrospector(KotlinNamesAnnotationIntrospector(this, cache, ignoredClassesForImplyingJsonCreator))
123123

124124
context.addDeserializers(KotlinDeserializers())
125+
context.addKeyDeserializers(KotlinKeyDeserializers)
125126
context.addSerializers(KotlinSerializers())
126127
context.addKeySerializers(KotlinKeySerializers())
127128

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@file:Suppress("EXPERIMENTAL_API_USAGE")
2-
31
package com.fasterxml.jackson.module.kotlin
42

53
import com.fasterxml.jackson.core.JsonGenerator
@@ -57,7 +55,6 @@ object ValueClassUnboxSerializer : StdSerializer<Any>(Any::class.java) {
5755
}
5856
}
5957

60-
@Suppress("EXPERIMENTAL_API_USAGE")
6158
internal class KotlinSerializers : Serializers.Base() {
6259
override fun findSerializer(
6360
config: SerializationConfig?,

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@file:Suppress("EXPERIMENTAL_API_USAGE")
2-
31
package com.fasterxml.jackson.module.kotlin
42

53
import java.math.BigInteger
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package com.fasterxml.jackson.module.kotlin.test
2+
3+
import com.fasterxml.jackson.core.exc.InputCoercionException
4+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
5+
import com.fasterxml.jackson.module.kotlin.readValue
6+
import org.junit.Assert.assertThrows
7+
import org.junit.Test
8+
import java.math.BigInteger
9+
import kotlin.test.assertEquals
10+
11+
internal class UnsignedNumbersOnKeyTest {
12+
companion object {
13+
val MAPPER = jacksonObjectMapper()
14+
}
15+
16+
class ForUByte {
17+
private fun makeSrc(v: Int): String = MAPPER.writeValueAsString(mapOf(v to 0))
18+
19+
@Test
20+
fun test() {
21+
val actual = MAPPER.readValue<Map<UByte, UByte>>(makeSrc(UByte.MAX_VALUE.toInt()))
22+
assertEquals(mapOf(UByte.MAX_VALUE to 0.toUByte()), actual)
23+
}
24+
25+
@Test
26+
fun overflow() {
27+
assertThrows(InputCoercionException::class.java) {
28+
MAPPER.readValue<Map<UByte, UByte>>(makeSrc(UByte.MAX_VALUE.toInt() + 1))
29+
}
30+
}
31+
32+
@Test
33+
fun underflow() {
34+
assertThrows(InputCoercionException::class.java) {
35+
MAPPER.readValue<Map<UByte, UByte>>(makeSrc(-1))
36+
}
37+
}
38+
}
39+
40+
class ForUShort {
41+
private fun makeSrc(v: Int): String = MAPPER.writeValueAsString(mapOf(v to 0))
42+
43+
@Test
44+
fun test() {
45+
val actual = MAPPER.readValue<Map<UShort, UShort>>(makeSrc(UShort.MAX_VALUE.toInt()))
46+
assertEquals(mapOf(UShort.MAX_VALUE to 0.toUShort()), actual)
47+
}
48+
49+
@Test
50+
fun overflow() {
51+
assertThrows(InputCoercionException::class.java) {
52+
MAPPER.readValue<Map<UShort, UShort>>(makeSrc(UShort.MAX_VALUE.toInt() + 1))
53+
}
54+
}
55+
56+
@Test
57+
fun underflow() {
58+
assertThrows(InputCoercionException::class.java) {
59+
MAPPER.readValue<Map<UShort, UShort>>(makeSrc(-1))
60+
}
61+
}
62+
}
63+
64+
class ForUInt {
65+
private fun makeSrc(v: Long): String = MAPPER.writeValueAsString(mapOf(v to 0))
66+
67+
@Test
68+
fun test() {
69+
val actual = MAPPER.readValue<Map<UInt, UInt>>(makeSrc(UInt.MAX_VALUE.toLong()))
70+
assertEquals(mapOf(UInt.MAX_VALUE to 0.toUInt()), actual)
71+
}
72+
73+
@Test
74+
fun overflow() {
75+
assertThrows(InputCoercionException::class.java) {
76+
MAPPER.readValue<Map<UInt, UInt>>(makeSrc(UInt.MAX_VALUE.toLong() + 1L))
77+
}
78+
}
79+
80+
@Test
81+
fun underflow() {
82+
assertThrows(InputCoercionException::class.java) {
83+
MAPPER.readValue<Map<UInt, UInt>>(makeSrc(-1L))
84+
}
85+
}
86+
}
87+
88+
class ForULong {
89+
private fun makeSrc(v: BigInteger): String = MAPPER.writeValueAsString(mapOf(v to 0))
90+
91+
@Test
92+
fun test() {
93+
val actual = MAPPER.readValue<Map<ULong, ULong>>(makeSrc(BigInteger(ULong.MAX_VALUE.toString())))
94+
assertEquals(mapOf(ULong.MAX_VALUE to 0.toULong()), actual)
95+
}
96+
97+
@Test
98+
fun overflow() {
99+
assertThrows(InputCoercionException::class.java) {
100+
MAPPER.readValue<Map<ULong, ULong>>(makeSrc(BigInteger(ULong.MAX_VALUE.toString()) + BigInteger.ONE))
101+
}
102+
}
103+
104+
@Test
105+
fun underflow() {
106+
assertThrows(InputCoercionException::class.java) {
107+
MAPPER.readValue<Map<ULong, ULong>>(makeSrc(BigInteger.valueOf(-1L)))
108+
}
109+
}
110+
}
111+
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
2-
31
package com.fasterxml.jackson.module.kotlin.test
42

53
import com.fasterxml.jackson.core.exc.InputCoercionException
@@ -92,4 +90,4 @@ internal class UnsignedNumbersTests {
9290
val json = mapper.writeValueAsString(-1)
9391
assertThrows(InputCoercionException::class.java) { mapper.readValue<ULong>(json) }
9492
}
95-
}
93+
}

src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github356.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ data class ClassWithInlineMember(val inlineClassProperty: InlineClass) {
5252
}
5353
}
5454

55-
@Suppress("EXPERIMENTAL_FEATURE_WARNING") // Enabled in test-compile
5655
@JvmInline
5756
value class ValueClass(val value: String)
5857

0 commit comments

Comments
 (0)